gpt4 book ai didi

java - 如何从 Trie 中检索给定长度的随机单词

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:48:42 24 4
gpt4 key购买 nike

我有一个简单的 Trie,我用它来存储大约 80k 个长度为 2 - 15 的单词。它非常适合检查一个字符串是否是一个单词;但是,现在我需要一种方法来获取给定长度的随机单词。换句话说,我需要“getRandomWord(5)”来返回一个 5 个字母的单词,所有 5 个字母的单词被返回的机会均等。

我能想到的唯一方法是选择一个随机数并以宽度优先的方式遍历树,直到我传递了那么多所需长度的单词。有更好的方法吗?

可能没有必要,但这是我的 trie 的代码。

class TrieNode {
private TrieNode[] c;
private Boolean end = false;

public TrieNode() {
c = new TrieNode[26];
}

protected void insert(String word) {
int n = word.charAt(0) - 'A';
if (c[n] == null)
c[n] = new TrieNode();
if (word.length() > 1) {
c[n].insert(word.substring(1));
} else {
c[n].end = true;
}
}

public Boolean isThisAWord(String word) {
if (word.length() == 0)
return false;
int n = word.charAt(0) - 'A';
if (c[n] != null && word.length() > 1)
return c[n].isThisAWord(word.substring(1));
else if (c[n] != null && c[n].end && word.length() == 1)
return true;
else
return false;
}
}

编辑:标记的答案效果很好;我将在此处添加我的实现以供后代使用,以防它对遇到类似问题的任何人有所帮助。

首先,我创建了一个辅助类来保存有关我在搜索中使用的 TrieNode 的元数据:

class TrieBranch {
TrieNode node;
int letter;
int depth;
public TrieBranch(TrieNode n, int l, int d) {
letter = l; node = n; depth = d;
}
}

这是保存 Trie 并实现随机词搜索的类。我是一个初学者,所以可能有更好的方法来做到这一点,但我对此进行了一些测试,它似乎有效。没有错误处理,所以买者自负。

class Dict {

final static int maxWordLength = 13;
final static int lettersInAlphabet = 26;
TrieNode trie;
int lengthFrequencyByLetter[][];
int totalLengthFrequency[];

public Dict() {
trie = new TrieNode();
lengthFrequencyByLetter = new int[lettersInAlphabet][maxWordLength + 1];
totalLengthFrequency = new int[maxWordLength + 1];
}

public String getRandomWord(int length) {
// Returns a random word of the specified length from the trie
// First, pick a random number from 0 to [number of words with this length]
Random r = new Random();
int wordIndex = r.nextInt(totalLengthFrequency[length]);

// figure out what the first letter of this word would be
int firstLetter = -1, totalSoFar = 0;
while (totalSoFar <= wordIndex) {
firstLetter++;
totalSoFar += lengthFrequencyByLetter[firstLetter][length];
}
wordIndex -= (totalSoFar - lengthFrequencyByLetter[firstLetter][length]);

// traverse the (firstLetter)'th node of trie depth-first to find the word (wordIndex)'th word
int[] result = new int[length + 1];
Stack<TrieBranch> stack = new Stack<TrieBranch>();
stack.push(new TrieBranch(trie.getBranch(firstLetter), firstLetter, 1));
while (!stack.isEmpty()) {
TrieBranch n = stack.pop();
result[n.depth] = n.letter;

// examine the current node
if (n.depth == length && n.node.isEnd()) {
wordIndex--;
if (wordIndex < 0) {
// search is over
String sResult = "";
for (int i = 1; i <= length; i++) {
sResult += (char)(result[i] + 'a');
}
return sResult;
}
}

// handle child nodes unless they're deeper than target length
if (n.depth < length) {
for (int i = 25; i >= 0; i--) {
if (n.node.getBranch(i) != null)
stack.push(new TrieBranch(n.node.getBranch(i), i, n.depth + 1));
}
}
}
return "failure of some sort";
}
}

使用普通字典(8 万个单词,最大长度 12)每次调用 getRandomWord() 大约需要 0.2 毫秒,而使用更详尽的字典(25 万个单词,最大长度 24)每次调用大约需要 1 毫秒。

最佳答案

为确保您有均匀的机会获得每个 5 个字母的单词,您需要知道树中有多少个 5 个字母的单词。因此,在构建树时,您将要添加的单词的长度添加到两个计数器:一个整体频率计数器和一个字母频率计数器:

int lengthFrequencyByLetter[letterIndex][maxWordLength-1]
int totalLengthFrequency[maxWordLength-1]

所以如果你有 4000 个 5 个字母的单词,其中 213 个以“d”开头,那么

lengthFrequencyByLetter[3][4] = 213

totalLengthFrequency[4] = 4000

将所有内容添加到树后。 (字母“a”为0,“b”为1,...“z”为25。)

从这里,您可以搜索给定 length 的第 n 个单词,其中 n 是从中选取的随机整数在 (0, totalLengthFrequency[length-1]) 范围内的均匀随机分布。

假设您的结构中有 4000 个由 5 个字母组成的单词。您选择随机数 1234。现在您可以检查

lengthFrequencyByLetter[0][4]
lengthFrequencyByLetter[1][4]
lengthFrequencyByLetter[2][4]
lengthFrequencyByLetter[3][4]

依次递增,直到总数超过1234。那么你很快就知道第1234个5个字母的单词的首字母是什么,然后去那里找。您不必每次都从头搜索树中的每个单词。

关于java - 如何从 Trie 中检索给定长度的随机单词,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17152269/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com