【问题标题】:How can I improve my Trie implementation in terms of initialization?如何在初始化方面改进我的 Trie 实现?
【发布时间】:2011-10-01 20:40:14
【问题描述】:

我正在尝试从一个庞大的单词列表中读入,并以一种允许我以后快速检索的方式存储它们。我首先想到的是使用 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()

【问题讨论】:

  • 我认为addWord 应该是insert_word
  • 啊,是的,很抱歉。不知道怎么没有改变:\
  • 你的单词列表有多大?
  • 如您所见,它从 /usr/share/dict/words 获取文件。我放了一个 len(word_list) ,它有 234937 个(我认为是独一无二的?)字

标签: python optimization data-structures trie


【解决方案1】:

在你考虑你的搜索工具是否工作之前,加速你的 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()

还有一个想法:除了能够确定查询词是否存在之外,您没有给出任何指示。如果这是正确的,您应该考虑将您的 DIY trie 与内置的set 进行基准测试。标准:加载速度(考虑从 pickle 中执行此操作)、查询速度和内存使用情况。

如果您确实希望获得比 bool 更多的检索信息,请将 dict 替换为 set 并重新阅读此答案。

如果您确实想在输入字符串中搜索单词,那么您可以考虑@unutbu 引用的代码,修复了错误并在find 函数中进行了一些加速(仅评估一次len(input),使用@987654332 @ 而不是 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    

或者您可以查看Aho-Corasick algorithm 中的Danny Yoo's fast implementation

【讨论】:

    【解决方案2】:

    有一个 Trie here 的替代实现。

    比较Trie.insert_wordbuild

    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,对于word 中的每个letterinsert_word 调用current_node.get_node(letter)。此方法有一个ifelse 块,并且每当到达else 块时都必须实例化一个新的TrieNode,然后将一个新的键值对插入到self.data 字典中。

    使用build,trie 本身只是一个字典。对于word 中的每个letter,只需调用一次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
    

    【讨论】:

    • -1: (a) 您修复了引用的build 函数中的一些错误,但没有提及您已修复它们。 (b) 你没有解决arg=mutable_object 问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多