Как создать TRIE в Python
Я новичок в Python и пытаюсь учиться и совершенствоваться. Меня интересуют попытки и DAWGs, и я много читал об этом, но я не понимаю, как должен выглядеть выходной файл TRIE или DAWG.
- должен ли TRIE быть объектом вложенных словарей? Где каждая буква
делится на буквы и так далее? - будет ли поиск, выполненный на таком словаре, быстрым, если есть записи 100k или 500k?
- как реализовать word-блоки состоит из более чем одного слова, разделенного-или пробелом?
- как связать префикс или суффикс слова с другой частью в структуре? [для DAWG]
Я хочу понять самое лучшее структура производства для того, чтобы выяснить, как создать и использовать один.
Я также был бы признателен, что должно быть выход чувак вместе с бор.
Я не хочу видеть графическое представление пузырьки связаны друг с другом, я видел их много во время чтения.
Я хотел бы знать выходной объект, как только набор слов превращается в попытки или DAWGs.
спасибо.
8 ответов:
отдыха по существу верно, что существует много различных способов реализации trie; и для большого, масштабируемого trie вложенные словари могут стать громоздкими или, по крайней мере, неэффективными в пространстве. Но так как вы только начинаете, я думаю, что это самый простой подход; вы можете закодировать простой
trieвсего в нескольких строках. Во-первых, функция для построения дерева:>>> _end = '_end_' >>> >>> def make_trie(*words): ... root = dict() ... for word in words: ... current_dict = root ... for letter in word: ... current_dict = current_dict.setdefault(letter, {}) ... current_dict[_end] = _end ... return root ... >>> make_trie('foo', 'bar', 'baz', 'barz') {'b': {'a': {'r': {'_end_': '_end_', 'z': {'_end_': '_end_'}}, 'z': {'_end_': '_end_'}}}, 'f': {'o': {'o': {'_end_': '_end_'}}}}если вы не знакомы с
setdefault, он просто смотрит вверх ключ в словарь (здесьletterили_end). Если ключ присутствует, он возвращает связанное значение; если нет, он присваивает значение по умолчанию для этого ключа и возвращает значение ({}или_end). (Это как версияgetэто также обновляет словарь.)далее, функция, чтобы проверить, является ли слово в боре. Это может быть более кратким, но я оставляю его многословным, чтобы логика была ясна:
>>> def in_trie(trie, word): ... current_dict = trie ... for letter in word: ... if letter in current_dict: ... current_dict = current_dict[letter] ... else: ... return False ... else: ... if _end in current_dict: ... return True ... else: ... return False ... >>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'baz') True >>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'barz') True >>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'barzz') False >>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'bart') False >>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'ba') Falseя оставлю вставки и удаления для вас упражнение.
конечно, предложение Unwind не будет намного сложнее. Там может быть небольшой недостаток скорости в том, что поиск правильного подузла потребует линейного поиска. Но поиск будет ограничен количеством возможных символов - 27, Если мы включим
_end. Кроме того, нет ничего, что можно было бы получить, создав массивный список узлов и получив доступ к ним по индексу, как он предлагает; вы также можете просто вложить списки.наконец, я добавлю, что создание DAWG было бы немного сложнее, потому что вам нужно обнаружить ситуации, в которых ваше текущее слово разделяет суффикс с другим словом в структуре. На самом деле, это может стать довольно сложным, в зависимости от того, как вы хотите структурировать DAWG! Возможно, вам придется узнать кое-что о Левенштейнарасстояние чтобы получить это право.
взгляните на это:
https://github.com/kmike/marisa-trie
статическая память-эффективные структуры Trie для Python (2.x и 3.икс.)
строковые данные в MARISA-trie могут занимать до 50x-100x меньше памяти, чем в стандартном Python dict; скорость поиска raw сопоставима; trie также предоставляет быстрые расширенные методы, такие как поиск префиксов.
на основе marisa-trie C++ библиотека.
вот сообщение в блоге от компании, использующей marisa trie successfully:
http://blog.repustate.com/sharing-large-data-structure-across-processes-python/в Repustate большая часть наших моделей данных, которые мы используем в нашем анализе текста, может быть представлена в виде простых пар ключ-значение или словарей на языке Python. В нашем конкретном случае, наши словари массивны, несколько сотен МБ каждый, и они должны быть доступны постоянно. Фактически для данного HTTP-запроса могут быть доступны 4 или 5 моделей, каждая из которых выполняет 20-30 поисков. Так проблема в том, как мы храним вещи быстро для клиента, а также как можно легче для сервера.
...
Я нашел этот пакет, marisa tries, который является оболочкой Python вокруг реализации C++ marisa trie. "Marisa" - это аббревиатура для алгоритма сопоставления с рекурсивно реализованным хранилищем. Что здорово о Мариса пытается это хранение механизм действительно сокращает, сколько памяти вам нужно. Автор плагина Python заявил о 50-100X уменьшении размера-наш опыт аналогичен.
что замечательно в пакете marisa trie, так это то, что базовая структура trie может быть записана на диск, а затем прочитана через объект, отображенный в памяти. С помощью карты памяти marisa trie все наши требования теперь выполнены. Использование памяти нашего сервера резко снизилось, примерно на 40%, и наша производительность не изменилась с тех пор, как мы реализация словарь в Python.
есть также несколько реализаций pure-python, хотя, если вы не находитесь на ограниченной платформе, вы хотели бы использовать C++ поддержанную реализацию выше для лучшей производительности:
вот список пакетов python, которые реализуют Trie:
- Мариса-трие - реализация на основе C++.
- python-trie - простая чистая реализация python.
- PyTrie - более продвинутая чистая реализация python.
нет никакого "должен"; это зависит от вас. Различные реализации будут иметь различные характеристики производительности, потребуется различное количество времени, чтобы реализовать, понять и получить право. Это типично для разработки программного обеспечения в целом, на мой взгляд.
Я бы, вероятно, сначала попытался создать глобальный список всех узлов trie до сих пор и представить дочерние указатели в каждом узле в виде списка индексов в глобальный список. Имея словарь только для представления ребенка связывание кажется мне слишком тяжелым.
изменена с
senderle' s метод (выше). Я нашел, что питонаdefaultdictидеально подходит для создания дерева trie или префикса.from collections import defaultdict class Trie: """ Implement a trie with insert, search, and startsWith methods. """ def __init__(self): self.root = defaultdict() # @param {string} word # @return {void} # Inserts a word into the trie. def insert(self, word): current = self.root for letter in word: current = current.setdefault(letter, {}) current.setdefault("_end") # @param {string} word # @return {boolean} # Returns if the word is in the trie. def search(self, word): current = self.root for letter in word: if letter not in current: return False current = current[letter] if "_end" in current: return True return False # @param {string} prefix # @return {boolean} # Returns if there is any word in the trie # that starts with the given prefix. def startsWith(self, prefix): current = self.root for letter in prefix: if letter not in current: return False current = current[letter] return True # Now test the class test = Trie() test.insert('helloworld') test.insert('ilikeapple') test.insert('helloz') print test.search('hello') print test.startsWith('hello') print test.search('ilikeapple')
Если вы хотите, чтобы TRIE был реализован как класс Python, вот что я написал после прочтения о них:
class Trie: def __init__(self): self.__final = False self.__nodes = {} def __repr__(self): return 'Trie<len={}, final={}>'.format(len(self), self.__final) def __getstate__(self): return self.__final, self.__nodes def __setstate__(self, state): self.__final, self.__nodes = state def __len__(self): return len(self.__nodes) def __bool__(self): return self.__final def __contains__(self, array): try: return self[array] except KeyError: return False def __iter__(self): yield self for node in self.__nodes.values(): yield from node def __getitem__(self, array): return self.__get(array, False) def create(self, array): self.__get(array, True).__final = True def read(self): yield from self.__read([]) def update(self, array): self[array].__final = True def delete(self, array): self[array].__final = False def prune(self): for key, value in tuple(self.__nodes.items()): if not value.prune(): del self.__nodes[key] if not len(self): self.delete([]) return self def __get(self, array, create): if array: head, *tail = array if create and head not in self.__nodes: self.__nodes[head] = Trie() return self.__nodes[head].__get(tail, create) return self def __read(self, name): if self.__final: yield name for key, value in self.__nodes.items(): yield from value.__read(name + [key])
эта версия использует рекурсию
import pprint from collections import deque pp = pprint.PrettyPrinter(indent=4) inp = raw_input("Enter a sentence to show as trie\n") words = inp.split(" ") trie = {} def trie_recursion(trie_ds, word): try: letter = word.popleft() out = trie_recursion(trie_ds.get(letter, {}), word) except IndexError: # End of the word return {} # Dont update if letter already present if not trie_ds.has_key(letter): trie_ds[letter] = out return trie_ds for word in words: # Go through each word trie = trie_recursion(trie, deque(word)) pprint.pprint(trie)выход:
Coool <algos> python trie.py Enter a sentence to show as trie foo bar baz fun { 'b': { 'a': { 'r': {}, 'z': {} } }, 'f': { 'o': { 'o': {} }, 'u': { 'n': {} } } }
from collections import defaultdictОпределить Trie:
_trie = lambda: defaultdict(_trie)Создать Trie:
trie = _trie() for s in ["cat", "bat", "rat", "cam"]: curr = trie for c in s: curr = curr[c] curr.setdefault("_end")Поиск:
def word_exist(trie, word): curr = trie for w in word: if w not in curr: return False curr = curr[w] return '_end' in curr
Comments