Je suis nouveau sur Python et j'essaie d'apprendre et d'avancer. Les TRIE et les DAWG m'intéressent et j'en ai beaucoup lu, mais je ne comprends pas à quoi devrait ressembler le fichier TRIE ou DAWG en sortie.
Je veux comprendre le meilleur structure de sortie afin de comprendre comment en créer et en utiliser un.
J'apprécierais également ce que devrait être le sortie d'un DAWG avec TRIE.
Je ne veux pas voir de représentations graphiques avec des bulles liées les unes aux autres, je les ai vues beaucoup en lisant.
J'aimerais connaître l'objet de sortie une fois qu'un ensemble de mots est transformé en TRIE ou DAWG.
Je vous remercie.
Unwind est essentiellement vrai qu'il existe de nombreuses façons différentes de mettre en œuvre un test; et, pour un grand nombre de projets évolutifs, les dictionnaires imbriqués peuvent devenir encombrants, ou du moins inefficaces. Mais comme vous venez tout juste de commencer, je pense que c'est l'approche la plus facile. vous pouvez coder une simple trie
en quelques lignes. Tout d'abord, une fonction pour construire le 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_'}}}}
Si vous n'êtes pas familier avec setdefault
, il cherchera simplement une clé dans le dictionnaire (ici, letter
ou _end
). Si la clé est présente, elle retourne la valeur associée. sinon, il attribue une valeur par défaut à cette clé et renvoie la valeur ({}
ou _end
). (C'est comme une version de get
qui met également à jour le dictionnaire.)
Ensuite, une fonction pour tester si le mot est dans le trie. Cela pourrait être plus concis, mais je le laisse verbeux pour que la logique soit claire:
>>> 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
Je vous laisse l'insertion et le retrait à titre d'exercice.
Bien sûr, la suggestion de Unwind ne serait pas beaucoup plus difficile. Il pourrait y avoir un léger inconvénient de vitesse dans la recherche du sous-nœud approprié nécessitant une recherche linéaire. Mais la recherche serait limitée au nombre de caractères possibles - 27 si nous incluons _end
. En outre, il n’ya rien à gagner à créer une liste massive de nœuds et à y accéder par index comme il le suggère; vous pourriez aussi bien imbriquer les listes.
Enfin, j'ajouterai que la création d'un DAWG serait un peu plus complexe car vous devez détecter les situations dans lesquelles votre mot Word actuel partage un suffixe avec un autre mot de la structure. En fait, cela peut devenir assez complexe, selon la manière dont vous souhaitez structurer le DAWG! Vous devrez peut-être apprendre des choses sur Levenshteindistance pour bien faire les choses.
Regardez ça:
https://github.com/kmike/marisa-trie
Structures Trie statiques à faible consommation de mémoire pour Python (2.x et 3.x).
Les données de chaîne dans un fichier MARISA-trie peuvent nécessiter jusqu'à 50 fois moins de mémoire que dans un dict Python standard; la vitesse de recherche brute est comparable; trie fournit également des méthodes avancées rapides comme la recherche par préfixe.
Basé sur la bibliothèque marisa-trie C++.
Voici un article de blog d'une entreprise utilisant marisa trie avec succès:
https://www.repustate.com/blog/sharing-large-data-structure-across-processes-python/
Chez Repustate, la plupart de nos modèles de données que nous utilisons dans notre analyse de texte peuvent être représentés sous forme de simples paires clé-valeur, ou dictionnaires dans Python Lingo. Dans notre cas particulier, nos dictionnaires sont volumineux, quelques centaines de Mo chacun, et ils doivent être consultés en permanence. En fait, pour une requête HTTP donnée, il est possible d'accéder à 4 ou 5 modèles, chacun effectuant 20 à 30 recherches. Le problème auquel nous sommes confrontés est donc de savoir comment garder les choses rapidement pour le client aussi claires que possible pour le serveur.
...
J'ai trouvé ce paquet, essaie marisa, qui est un wrapper Python autour d'une implémentation C++ d'un trie marisa. «Marisa» est un acronyme pour Matching Algorithm avec StorAge implémenté de manière récursive. Ce qui est génial avec les essais marisa, c’est que le mécanisme de stockage réduit considérablement la quantité de mémoire dont vous avez besoin. L'auteur du plug-in Python a déclaré une réduction de taille de 50 à 100 fois - notre expérience est similaire.
Ce qui est génial avec le paquet marisa trie, c’est que la structure de test sous-jacente peut être écrite sur le disque, puis lue via un objet mappé en mémoire. Avec une mémoire marisa trie cartographiée, toutes nos exigences sont maintenant satisfaites. La mémoire utilisée par notre serveur a considérablement diminué, d’environ 40%, et nos performances sont restées inchangées par rapport à la mise en œuvre du dictionnaire Python.
Il existe également quelques implémentations en python pur, bien que, sauf sur une plate-forme restreinte, vous souhaitiez utiliser l'implémentation sécurisée C++ décrite ci-dessus pour optimiser les performances:
Voici une liste des paquets python qui implémentent Trie:
Il n'y a pas de "devrait"; c'est à vous. Diverses implémentations auront des caractéristiques de performance différentes, prendront beaucoup de temps à mettre en œuvre, à comprendre et à réussir. Ceci est typique pour le développement logiciel dans son ensemble, à mon avis.
J'essaierais probablement d'abord de créer une liste globale de tous les noeuds, et de représenter les pointeurs enfants de chaque noeud sous forme de liste d'index dans la liste globale. Avoir un dictionnaire juste pour représenter le lien enfant me semble trop lourd.
Modifié à partir de la méthode senderle
(ci-dessus). J'ai trouvé que defaultdict
de Python est idéal pour créer un tri ou un arbre de préfixes.
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')
Si vous voulez qu'un TRIE soit implémenté en tant que classe Python, voici ce que j'ai écrit après avoir lu à leur sujet:
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])
Cette version utilise la récursivité
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)
Sortie:
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 = lambda: defaultdict(_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
print(Word_exist(trie, 'cam'))
class Trie:
head = {}
def add(self,Word):
cur = self.head
for ch in Word:
if ch not in cur:
cur[ch] = {}
cur = cur[ch]
cur['*'] = True
def search(self,Word):
cur = self.head
for ch in Word:
if ch not in cur:
return False
cur = cur[ch]
if '*' in cur:
return True
else:
return False
def printf(self):
print (self.head)
dictionary = Trie()
dictionary.add("hi")
#dictionary.add("hello")
#dictionary.add("eye")
#dictionary.add("hey")
print(dictionary.search("hi"))
print(dictionary.search("hello"))
print(dictionary.search("hel"))
print(dictionary.search("he"))
dictionary.printf()
En dehors
True
False
False
False
{'h': {'i': {'*': True}}}