我正在尝试从一个巨大的单词列表中读取并以一种允许我稍后快速检索的方式存储它们。我首先想到使用trie,我会承认我的实现很幼稚,它基本上是嵌套的哈希表,每个键都是不同的字母。现在,在trie中插入一个单词需要花费很长时间(运行此程序需要20多秒),我想知道是否有人有任何想法可以做些什么来改善我的插入?这不是作业。
import string
import time
class Trie:
def __init__(self):
self.root = TrieNode()
def insert_word(self, word):
current_node = self.root
for letter in word:
trie_node = current_node.get_node(letter)
current_node = trie_node
class TrieNode:
def __init__(self):
self.data = {}
def get_node(self, letter):
if letter in self.data:
return self.data[letter]
else:
new_trie_node = TrieNode()
self.data[letter] = new_trie_node
return new_trie_node
def main():
start_time = time.time()
trie = Trie()
with open('/usr/share/dict/words', 'r') as dictionary:
word_list = dictionary.read()
word_list = word_list.split("\n")
for word in word_list:
trie.insert_word(word.lower())
print time.time() - start_time, "seconds"
if __name__ == "__main__":
main()
答案 0 :(得分:0)
在考虑您的搜索工具是否正常工作之前,加速您的trie初始化是完全没有意义的。
在@unutbu提到的代码中,为什么你认为它与{'end':False}
和pt['end']=True
混在一起?
以下是您的一些测试数据:
words_to_insert = ['foo', 'foobar']
queries_expecting_true = words_to_insert
queries_expecting_false = "fo foe foob food foobare".split()
这是另一个想法:你没有表明你想要的东西比确定是否存在查询词的能力更多。如果这是正确的,您应该考虑使用内置的set
对DIY trie进行基准测试。标准:加载速度(考虑从泡菜中执行此操作),查询速度和内存使用情况。
如果您确实需要检索的内容超过bool
,请将dict
替换为set
并重新阅读此答案。
如果您确实想在输入字符串中搜索单词,那么您可以考虑@unutbu引用的代码,修复了错误并在find
函数中加了一些加速(仅评估len(input)
一次) ,使用xrange
代替range
(Python 2.x))并删除不必要的TERMINAL: False
条目:
TERMINAL = None # Marks the end of a word
def build(words, trie=None): # bugs fixed
if trie is None:
trie = {}
for word in words:
if not word: continue # bug fixed
pt = trie # bug fixed
for ch in word:
pt = pt.setdefault(ch, {})
pt[TERMINAL] = True
return trie
def find(input, trie):
len_input = len(input)
results = []
for i in xrange(len_input):
pt = trie
for j in xrange(i, len_input + 1):
if TERMINAL in pt:
results.append(input[i:j])
if j >= len_input or input[j] not in pt:
break
pt = pt[input[j]]
return results
或者您可以查看Danny Yoo's fast implementation的Aho-Corasick algorithm。
答案 1 :(得分:-1)
还有Trie here的替代实现。
将Trie.insert_word
与build
进行比较:
def build(words,trie={'end':False}):
'''
build builds a trie in O(M*L) time, where
M = len(words)
L = max(map(len,words))
'''
for word in words:
pt=trie
for letter in word:
pt=pt.setdefault(letter, {'end':False})
pt['end']=True
return trie
使用Trie
,对于letter
中的每个word
,insert_word
调用current_node.get_node(letter)
。此方法具有if
和else
块,并且必须在到达TrieNode
块时实例化新的else
,然后将新的键值对插入到self.data
dict。
使用build
,特里本身就是一个字典。对于letter
中的每个word
,只需拨打pt.setdefault(...)
一次。 dict
方法在C中实现,并且比在Python中实现类似代码更快。
timeit
显示速度差异大约2倍(赞成build
):
def alt_main():
with open('/usr/share/dict/words', 'r') as dictionary:
word_list = dictionary.read()
word_list = word_list.split("\n")
return build(word_list)
% python -mtimeit -s'import test' 'test.main()'
10 loops, best of 3: 1.16 sec per loop
% python -mtimeit -s'import test' 'test.alt_main()'
10 loops, best of 3: 571 msec per loop