La deuxième solution d'Anna est la source d'inspiration de celle-ci.
Tout d'abord, il faut charger tous les mots dans la mémoire et diviser le dictionnaire en sections en fonction de la longueur des mots.
Pour chaque longueur, faire n des copies d'un tableau de pointeurs sur les mots. Triez chaque tableau de manière à ce que les chaînes apparaissent dans l'ordre lorsqu'il est tourné d'un certain nombre de lettres . Par exemple, supposons que la liste originale de mots de 5 lettres soit [plane, apple, space, train, happy, stack, hacks]. Vos cinq tableaux de pointeurs seront alors les suivants :
rotated by 0 letters: [apple, hacks, happy, plane, space, stack, train]
rotated by 1 letter: [hacks, happy, plane, space, apple, train, stack]
rotated by 2 letters: [space, stack, train, plane, hacks, apple, happy]
rotated by 3 letters: [space, stack, train, hacks, apple, plane, happy]
rotated by 4 letters: [apple, plane, space, stack, train, hacks, happy]
(Au lieu de pointeurs, vous pouvez utiliser des entiers identifiant les mots, si cela vous permet d'économiser de l'espace sur votre plate-forme).
Pour effectuer une recherche, il suffit de demander à combien s'élèverait la rotation de la modèle de manière à ce que les points d'interrogation apparaissent à la fin. Vous pouvez ensuite effectuer une recherche binaire dans la liste appropriée.
Si vous devez trouver des correspondances pour ??ppy, vous devrez faire une rotation de 2 pour obtenir ppy ?? Il faut donc chercher dans le tableau ce qui est dans l'ordre lorsqu'on le fait pivoter de 2 lettres. Une recherche binaire rapide révèle que "happy" est la seule correspondance.
Si vous devez trouver des correspondances pour th??g, vous devrez faire une rotation de 4 pour obtenir gth ? Regardez donc dans le tableau 4, où une recherche binaire trouve qu'il n'y a pas de correspondance.
Cela fonctionne quel que soit le nombre de points d'interrogation, à condition qu'ils apparaissent tous ensemble.
Espace nécessaire en plus du dictionnaire lui-même : Pour les mots de longueur N, il faut de l'espace pour (N fois le nombre de mots de longueur N) pointeurs ou entiers.
Temps par consultation : O(log n) où n est le nombre de mots de la longueur appropriée.
Mise en œuvre en Python :
import bisect
class Matcher:
def __init__(self, words):
# Sort the words into bins by length.
bins = []
for w in words:
while len(bins) <= len(w):
bins.append([])
bins[len(w)].append(w)
# Make n copies of each list, sorted by rotations.
for n in range(len(bins)):
bins[n] = [sorted(bins[n], key=lambda w: w[i:]+w[:i]) for i in range(n)]
self.bins = bins
def find(self, pattern):
bins = self.bins
if len(pattern) >= len(bins):
return []
# Figure out which array to search.
r = (pattern.rindex('?') + 1) % len(pattern)
rpat = (pattern[r:] + pattern[:r]).rstrip('?')
if '?' in rpat:
raise ValueError("non-adjacent wildcards in pattern: " + repr(pattern))
a = bins[len(pattern)][r]
# Binary-search the array.
class RotatedArray:
def __len__(self):
return len(a)
def __getitem__(self, i):
word = a[i]
return word[r:] + word[:r]
ra = RotatedArray()
start = bisect.bisect(ra, rpat)
stop = bisect.bisect(ra, rpat[:-1] + chr(ord(rpat[-1]) + 1))
# Return the matches.
return a[start:stop]
words = open('/usr/share/dict/words', 'r').read().split()
print "Building matcher..."
m = Matcher(words) # takes 1-2 seconds, for me
print "Done."
print m.find("st??k")
print m.find("ov???low")
Sur mon ordinateur, le dictionnaire système fait 909 Ko et ce programme utilise environ 3,2 Mo de mémoire en plus de ce qu'il faut pour stocker les mots (les pointeurs font 4 octets). Pour ce dictionnaire, vous pourriez réduire cette consommation de moitié en utilisant des entiers de 2 octets à la place des pointeurs, car il y a moins de 2 16 mots de chaque longueur.
Mesures : Sur ma machine, m.find("st??k")
s'exécute en 0,000032 seconde, m.find("ov???low")
en 0,000034 seconde, et m.find("????????????????e")
en 0,000023 seconde.
En écrivant la recherche binaire au lieu d'utiliser la fonction class RotatedArray
et le bisect
j'ai ramené les deux premiers chiffres à 0,000016 seconde, soit deux fois plus vite. L'implémentation de cette méthode en C++ la rendrait encore plus rapide.
11 votes
Pourquoi ne les transformez-vous pas en expressions régulières et en recherches ? Qu'espérez-vous ? Quelles sont vos attentes ? Quelles sont vos contraintes ?
3 votes
Quelle serait la rapidité des expressions régulières ? Je sais ce que c'est, mais je ne sais pas comment cela fonctionne. Je peux simplement parcourir l'ensemble du dictionnaire, mais ce serait Theta(N). Je me demandais si je pouvais faire mieux.
0 votes
Quelle est la structure de votre dictionnaire ?
0 votes
Pour l'instant, il s'agit simplement d'un fichier texte contenant tous les mots classés par ordre alphabétique.
2 votes
@Danny : Mise à jour de la question. Veuillez ne pas commenter une question qui vous appartient. La question vous appartient. Vous pouvez la mettre à jour pour qu'elle contienne tous l'information. Veuillez mettre à jour la question.
0 votes
Combien de mots y a-t-il dans le dictionnaire ? Quelle est l'étendue des longueurs ? Quel est l'alphabet utilisé ?
0 votes
Pourquoi, exactement, une tâche à faible encombrement serait-elle trop lente ? Vous attendez-vous à charger plus de données que la mémoire disponible et donc à créer de nombreuses erreurs de page ?
0 votes
Il s'agit du dictionnaire anglais qui contient entre 200 et 500 000 mots.
2 votes
Il semble que la solution que vous avez ajoutée à la question soit équivalente à la première suggestion d'Anna (le hachage), sauf qu'il peut y avoir des collisions indésirables. Si vous passez simplement à sa suggestion, vous utiliserez à peu près la même quantité de mémoire (c'est-à-dire beaucoup), mais vous n'aurez pas à vérifier à chaque fois s'il y a des collisions dans l'ensemble du seau de hachage, ce qui devrait vous permettre d'être beaucoup plus rapide.
1 votes
Ne seriez-vous pas en train de créer une IA de scrabble ? (Puisqu'il y a un maximum de 2 blancs dans le jeu...)
0 votes
@int3 - mais la question dit que les blancs doivent être adjacents, ce qui n'est pas vrai au Scrabble.
2 votes
1e-9 est une nanoseconde - c'est à peu près le temps qu'il faut à un PC normal pour additionner deux nombres. Il n'y a rien de mal dans votre algorithme, ce dont vous avez besoin, c'est d'un superordinateur.