Dernièrement, j'ai joué à un jeu sur mon iPhone appelé Scramble. Certains d'entre vous connaissent peut-être ce jeu sous le nom de Boggle. Essentiellement, lorsque le jeu commence, vous obtenez une matrice de lettres comme ceci:
F X I E
A M L O
E W B X
A S T U
Le but du jeu est de trouver autant de mots que possible qui peuvent être formés en enchaînant des lettres. Vous pouvez commencer par n'importe quelle lettre, et toutes les lettres qui l'entourent sont du jeu équitable, puis une fois que vous passez à la lettre suivante, toutes les lettres qui entourent cette lettre sont du jeu équitable, à l' exception des lettres précédemment utilisées . Ainsi , dans la grille ci - dessus, par exemple, je pourrais venir avec les mots LOB
, TUX
, SEA
, FAME
, etc. Les mots doivent être au moins 3 caractères et pas plus de caractères NxN, qui serait 16 dans ce jeu , mais peut varier dans certaines implémentations . Bien que ce jeu soit amusant et addictif, je ne suis apparemment pas très bon et je voulais tricher un peu en créant un programme qui me donnerait les meilleurs mots possibles (plus le mot est long, plus vous obtenez de points).
(source: boggled.org )
Je ne suis malheureusement pas très bon avec les algorithmes ou leur efficacité et ainsi de suite. Ma première tentative utilise un dictionnaire tel que celui-ci (~ 2,3 Mo) et effectue une recherche linéaire en essayant de faire correspondre les combinaisons avec les entrées du dictionnaire. Cela prend beaucoup de temps pour trouver les mots possibles, et comme vous ne disposez que de 2 minutes par tour, ce n'est tout simplement pas suffisant.
Je suis intéressé de voir si des Stackoverflowers peuvent proposer des solutions plus efficaces. Je recherche principalement des solutions utilisant les Big 3 Ps: Python, PHP et Perl, bien que tout avec Java ou C ++ soit cool aussi, car la vitesse est essentielle.
SOLUTIONS ACTUELLES :
- Adam Rosenfield, Python, ~ 20s
- John Fouhy, Python, ~ 3s
- Kent Fredric, Perl, ~ 1 s
- Bacon Darius, Python, ~ 1 s
- rvarcher, VB.NET (lien en direct) , ~ 1s
- Paolo Bergantino, PHP (lien en direct) , ~ 5s (~ 2s localement)
Réponses:
Ma réponse fonctionne comme les autres ici, mais je la publierai car elle semble un peu plus rapide que les autres solutions Python, de la configuration du dictionnaire plus rapidement. (J'ai vérifié cela par rapport à la solution de John Fouhy.) Après la configuration, le temps de résolution est dans le bruit.
Exemple d'utilisation:
Modifier: filtrez les mots de moins de 3 lettres.
Edit 2: J'étais curieux de savoir pourquoi la solution Perl de Kent Fredric était plus rapide; il s'avère utiliser une correspondance d'expressions régulières au lieu d'un ensemble de caractères. Faire de même en Python pour doubler la vitesse.
la source
def neighbors((x, y))
(inutilement, pour autant que je puisse voir). Il nécessite également des parenthèses autour de l'argument toprint
.La solution la plus rapide que vous obtiendrez consistera probablement à stocker votre dictionnaire dans un trie . Ensuite, créez une file d'attente de triplets ( x , y , s ), où chaque élément de la file d'attente correspond à un préfixe s d'un mot qui peut être orthographié dans la grille, se terminant à l'emplacement ( x , y ). Initialisez la file d'attente avec N x N éléments (où N est la taille de votre grille), un élément pour chaque carré de la grille. Ensuite, l'algorithme procède comme suit:
Si vous stockez votre dictionnaire dans un trie, tester si s + c est un mot ou un préfixe d'un mot peut être fait en temps constant (à condition de conserver également des métadonnées supplémentaires dans chaque donnée de file d'attente, comme un pointeur vers le nœud actuel dans le trie), donc le temps d'exécution de cet algorithme est O (nombre de mots qui peuvent être épelés).
[Edit] Voici une implémentation en Python que je viens de coder:
Exemple d'utilisation:
Production:
Remarques: Ce programme ne produit pas de mots à 1 lettre ou ne filtre pas du tout par longueur de mot. C'est facile à ajouter mais pas vraiment pertinent au problème. Il génère également plusieurs mots plusieurs fois s'ils peuvent être orthographiés de plusieurs manières. Si un mot donné peut être orthographié de différentes manières (pire des cas: chaque lettre de la grille est la même (par exemple 'A') et un mot comme 'aaaaaaaaaa' est dans votre dictionnaire), alors le temps d'exécution deviendra horriblement exponentiel . Le filtrage des doublons et du tri est trivial car une fois l'algorithme terminé.
la source
(x,y)
, un adepte possible est(x+1,y+1)
. Puis(x+1,y+1)
est poussé dans la file d'attente. Mais(x,y)
sera également un adepte(x+1,y+1)
, donc cela ne conduira-t-il pas à un rebond sans fin entre eux?Pour une accélération de dictionnaire, il y a une transformation / processus général que vous pouvez faire pour réduire considérablement les comparaisons de dictionnaire à l'avance.
Étant donné que la grille ci-dessus ne contient que 16 caractères, certains d'entre eux en double, vous pouvez réduire considérablement le nombre de clés totales dans votre dictionnaire en filtrant simplement les entrées qui ont des caractères inaccessibles.
Je pensais que c'était l'optimisation évidente mais vu que personne ne l'a fait, je le mentionne.
Cela m'a réduit d'un dictionnaire de 200 000 clés à seulement 2 000 clés simplement pendant la passe d'entrée. Cela réduit à tout le moins la surcharge de la mémoire, et c'est sûr de correspondre à une augmentation de vitesse quelque part car la mémoire n'est pas infiniment rapide.
Implémentation de Perl
Mon implémentation est un peu lourde car j'ai accordé de l'importance à pouvoir connaître le chemin exact de chaque chaîne extraite, pas seulement la validité qui s'y trouve.
J'ai également quelques adaptations qui permettraient théoriquement à une grille avec des trous de fonctionner, et des grilles avec des lignes de tailles différentes (en supposant que vous obtenez la bonne entrée et qu'elle s'aligne d'une manière ou d'une autre).
Le filtre précoce est de loin le goulot d' étranglement le plus important dans mon application, comme on le soupçonnait plus tôt, commentant que la ligne le gonfle de 1,5 s à 7,5 s.
Lors de l'exécution, il semble penser que tous les chiffres sont sur leurs propres mots valides, mais je suis presque sûr que c'est dû au fonctionnement du fichier dictionnaire.
C'est un peu gonflé, mais au moins je réutilise Tree :: Trie de cpan
Certains d'entre eux ont été inspirés en partie par les implémentations existantes, certains j'avais déjà en tête.
La critique constructive et les façons de l'améliorer sont les bienvenues (/ moi note qu'il n'a jamais recherché CPAN pour un résolveur de boggles , mais c'était plus amusant à travailler)
mis à jour pour de nouveaux critères
Arch / information d'exécution pour comparaison:
Plus de marmonnements sur cette optimisation Regex
L'optimisation des expressions rationnelles que j'utilise est inutile pour les dictionnaires multi-résolutions, et pour les multi-résolutions, vous voudrez un dictionnaire complet, pas pré-découpé.
Cependant, cela dit, pour les résolutions ponctuelles, c'est vraiment rapide. (Les expressions rationnelles de Perl sont en C! :))
Voici quelques ajouts de code différents:
ps: 8,16 * 200 = 27 minutes.
la source
Vous pouvez diviser le problème en deux:
Idéalement, (2) devrait également inclure un moyen de tester si une chaîne est un préfixe d'un mot valide - cela vous permettra d'élaguer votre recherche et de gagner un tas de temps.
Trie d'Adam Rosenfield est une solution à (2). C'est élégant et probablement ce que votre spécialiste en algorithmes préférerait, mais avec les langages modernes et les ordinateurs modernes, nous pouvons être un peu plus paresseux. De plus, comme Kent le suggère, nous pouvons réduire la taille de notre dictionnaire en supprimant les mots dont les lettres ne sont pas présentes dans la grille. Voici du python:
Sensationnel; test de préfixe à temps constant. Il faut quelques secondes pour charger le dictionnaire que vous avez lié, mais seulement quelques :-) (remarquez que
words <= prefixes
)Maintenant, pour la partie (1), je suis enclin à penser en termes de graphiques. Je vais donc créer un dictionnaire qui ressemble à ceci:
c'est-à
graph[(x, y)]
- dire l'ensemble des coordonnées que vous pouvez atteindre à partir de la position(x, y)
. J'ajouterai également un nœud facticeNone
qui se connectera à tout.Construire, c'est un peu maladroit, car il y a 8 positions possibles et vous devez faire une vérification des limites. Voici un code python maladroit correspondant:
Ce code crée également un mappage de dictionnaire
(x,y)
avec le caractère correspondant. Cela me permet de transformer une liste de postes en un mot:Enfin, nous effectuons une recherche en profondeur. La procédure de base est la suivante:
Python:
Exécutez le code en tant que:
et inspectez
res
pour voir les réponses. Voici une liste de mots trouvés pour votre exemple, triés par taille:Le code prend (littéralement) quelques secondes pour charger le dictionnaire, mais le reste est instantané sur ma machine.
la source
range(len(w)+1)
place derange(len(w))
. Je l'ai affirméwords <= prefixes
mais apparemment, je n'ai pas testé cela: - /Ma tentative en Java. Il faut environ 2 s pour lire le fichier et construire le trie, et environ 50 ms pour résoudre le puzzle. J'ai utilisé le dictionnaire lié dans la question (il a quelques mots que je ne connaissais pas en anglais comme fae, ima)
Le code source se compose de 6 classes. Je les posterai ci-dessous (si ce n'est pas la bonne pratique sur StackOverflow, dites-le moi).
gineer.bogglesolver.Main
gineer.bogglesolver.Solver
gineer.bogglesolver.trie.Node
gineer.bogglesolver.trie.Trie
gineer.bogglesolver.util.Constants
gineer.bogglesolver.util.Util
la source
Je pense que vous passerez probablement la plupart de votre temps à essayer de faire correspondre des mots qui ne peuvent pas être créés par votre grille de lettres. Donc, la première chose que je ferais est d'essayer d'accélérer cette étape et cela devrait vous aider à faire la plupart du chemin.
Pour cela, je voudrais ré-exprimer la grille sous forme de tableau des "mouvements" possibles que vous indexez par la transition de lettre que vous regardez.
Commencez par attribuer à chaque lettre un numéro de tout votre alphabet (A = 0, B = 1, C = 2, ... et ainsi de suite).
Prenons cet exemple:
Et pour l'instant, utilisons l'alphabet des lettres que nous avons (généralement, vous voudrez probablement utiliser le même alphabet entier à chaque fois):
Ensuite, vous créez un tableau booléen 2D qui indique si vous avez une certaine transition de lettre disponible:
Parcourez maintenant votre liste de mots et convertissez les mots en transitions:
Vérifiez ensuite si ces transitions sont autorisées en les recherchant dans votre table:
S'ils sont tous autorisés, il y a une chance que ce mot soit trouvé.
Par exemple, le mot "casque" peut être exclu lors de la 4ème transition (m à e: helMEt), car cette entrée dans votre tableau est fausse.
Et le mot hamster peut être exclu, car la première transition (h vers a) n'est pas autorisée (n'existe même pas dans votre table).
Maintenant, pour les quelques mots probablement que vous n'avez pas éliminés, essayez de les trouver dans la grille comme vous le faites maintenant ou comme suggéré dans certaines des autres réponses ici. Ceci afin d'éviter les faux positifs résultant de sauts entre des lettres identiques dans votre grille. Par exemple, le mot "aide" est autorisé par le tableau, mais pas par la grille.
Quelques autres conseils d'amélioration des performances sur cette idée:
Au lieu d'utiliser un tableau 2D, utilisez un tableau 1D et calculez simplement l'index de la deuxième lettre vous-même. Donc, au lieu d'un tableau 12x12 comme ci-dessus, faites un tableau 1D de longueur 144. Si vous utilisez ensuite toujours le même alphabet (c'est-à-dire un tableau 26x26 = 676x1 pour l'alphabet anglais standard), même si toutes les lettres n'apparaissent pas dans votre grille , vous pouvez pré-calculer les indices dans ce tableau 1D que vous devez tester pour faire correspondre vos mots de dictionnaire. Par exemple, les indices de «bonjour» dans l'exemple ci-dessus seraient
Étendez l'idée à une table 3D (exprimée sous forme de tableau 1D), c'est-à-dire toutes les combinaisons de 3 lettres autorisées. De cette façon, vous pouvez éliminer encore plus de mots immédiatement et vous réduisez le nombre de recherches de tableau pour chaque mot de 1: Pour 'hello', vous n'avez besoin que de 3 recherches de tableau: hel, ell, llo. Soit dit en passant, il sera très rapide de construire ce tableau, car il n'y a que 400 mouvements de 3 lettres possibles dans votre grille.
Pré-calculez les indices des mouvements dans votre grille que vous devez inclure dans votre table. Pour l'exemple ci-dessus, vous devez définir les entrées suivantes sur «True»:
Je suis sûr que si vous utilisez cette approche, vous pouvez exécuter votre code incroyablement rapidement, si vous avez le dictionnaire pré-calculé et déjà chargé en mémoire.
BTW: Une autre bonne chose à faire, si vous construisez un jeu, est d'exécuter ce genre de choses immédiatement en arrière-plan. Commencez à générer et à résoudre le premier jeu pendant que l'utilisateur regarde toujours l'écran de titre de votre application et met son doigt en position pour appuyer sur "Play". Ensuite, générez et résolvez le jeu suivant pendant que l'utilisateur joue le précédent. Cela devrait vous donner beaucoup de temps pour exécuter votre code.
(J'aime ce problème, donc je serai probablement tenté d'implémenter ma proposition en Java dans les prochains jours pour voir comment cela fonctionnerait réellement ... Je posterai le code ici une fois que je le ferai.)
MISE À JOUR:
Ok, j'ai eu un peu de temps aujourd'hui et j'ai implémenté cette idée en Java:
Voici quelques résultats:
Pour la grille de l'image postée dans la question d'origine (DGHI ...):
Pour les lettres affichées comme exemple dans la question d'origine (FXIE ...)
Pour la grille 5x5 suivante:
cela donne ceci:
Pour cela, j'ai utilisé la liste de mots du scrabble du tournoi TWL06 , car le lien dans la question d'origine ne fonctionne plus. Ce fichier fait 1,85 Mo, il est donc un peu plus court. Et la
buildDictionary
fonction jette tous les mots de moins de 3 lettres.Voici quelques observations sur les performances de ceci:
C'est environ 10 fois plus lent que les performances rapportées de l'implémentation OCaml de Victor Nicollet. Que cela soit dû à l'algorithme différent, au dictionnaire plus court qu'il a utilisé, au fait que son code soit compilé et que le mien s'exécute dans une machine virtuelle Java, ou aux performances de nos ordinateurs (le mien est un Intel Q6600 @ 2,4 MHz exécutant WinXP), Je ne sais pas. Mais c'est beaucoup plus rapide que les résultats des autres implémentations cités à la fin de la question d'origine. Donc, que cet algorithme soit supérieur au dictionnaire trie ou non, je ne sais pas pour l'instant.
La méthode du tableau utilisée dans
checkWordTriplets()
donne une très bonne approximation des réponses réelles. Seul 1 mot sur 3 à 5 réussi échoueracheckWords()
(voir le nombre de candidats par rapport au nombre de mots réels ci-dessus).Quelque chose que vous ne pouvez pas voir ci-dessus: La
checkWordTriplets()
fonction prend environ 3,65 ms et est donc pleinement dominante dans le processus de recherche. LacheckWords()
fonction prend à peu près les 0,05-0,20 ms restantes.Le temps d'exécution de la
checkWordTriplets()
fonction dépend linéairement de la taille du dictionnaire et est pratiquement indépendant de la taille de la carte!Le temps d'exécution de
checkWords()
dépend de la taille du tableau et du nombre de mots non exclus parcheckWordTriplets()
.L'
checkWords()
implémentation ci-dessus est la première version la plus stupide que j'ai trouvée. Il n'est fondamentalement pas du tout optimisé. Mais par rapport àcheckWordTriplets()
cela, cela n'a pas d'importance pour la performance totale de l'application, donc je ne m'en suis pas inquiété. Mais , si la taille de la carte augmente, cette fonction deviendra de plus en plus lente et finira par devenir importante. Ensuite, il devrait également être optimisé.Une bonne chose à propos de ce code est sa flexibilité:
initializeBoard()
.D'accord, mais je pense que ce post est maintenant assez long. Je peux certainement répondre à toutes vos questions, mais passons aux commentaires.
la source
ok = possibleTriplets[entry.triplets[t]];
. hmm?entry.letters[i] = (byte) letter - 65;
il prend simplement la valeur ASCII et soustrait 65 ("A"). Si vous avez des trémas ou des lettres minuscules dans votre dictionnaire, cela donnera des valeurs supérieures à 31, qui ne sont pas prévues par le réglage de la taille de l'alphabet à la ligne 9. Pour prendre en charge d'autres lettres, vous devrez développer cette ligne pour les mapper dans la plage autorisée par la taille de l'alphabet.Étonnamment, personne n'a tenté une version PHP de cela.
Il s'agit d'une version PHP fonctionnelle de la solution Python de John Fouhy's.
Bien que j'aie pris quelques pointeurs des réponses de tout le monde, c'est surtout copié de John.
Voici un lien en direct si vous voulez l'essayer. Bien que cela prenne environ 2 secondes sur ma machine locale, cela prend environ 5 secondes sur mon serveur Web. Dans les deux cas, ce n'est pas très rapide. Pourtant, c'est assez hideux, donc je peux imaginer que le temps peut être considérablement réduit. Tout conseil sur la façon d'y parvenir serait apprécié. Le manque de tuples de PHP a rendu les coordonnées étranges avec lesquelles travailler et mon incapacité à comprendre exactement ce qui se passe n'a pas aidé du tout.
EDIT : Quelques corrections font qu'il faut moins de 1s localement.
la source
Pas intéressé par VB? :) Je n'ai pas pu résister. J'ai résolu cela différemment de la plupart des solutions présentées ici.
Mes horaires sont:
EDIT: les temps de chargement du dictionnaire sur le serveur hôte Web s'exécutent environ 1 à 1,5 seconde de plus que mon ordinateur personnel.
Je ne sais pas à quel point les temps se détérioreront avec une charge sur le serveur.
J'ai écrit ma solution sous forme de page Web en .Net. myvrad.com/boggle
J'utilise le dictionnaire référencé dans la question d'origine.
Les lettres ne sont pas réutilisées dans un mot. Seuls les mots de 3 caractères ou plus sont trouvés.
J'utilise une table de hachage de tous les préfixes et mots de mots uniques au lieu d'un trie. Je ne connaissais pas les trios, alors j'ai appris quelque chose là-bas. L'idée de créer une liste de préfixes de mots en plus des mots complets est ce qui a finalement réduit mon temps à un nombre respectable.
Lisez les commentaires du code pour plus de détails.
Voici le code:
la source
Dès que j'ai vu l'énoncé du problème, j'ai pensé "Trie". Mais vu que plusieurs autres affiches utilisaient cette approche, j'ai cherché une autre approche juste pour être différente. Hélas, l'approche Trie est plus performante. J'ai exécuté la solution Perl de Kent sur ma machine et il a fallu 0,31 seconde pour l'exécuter, après l'avoir adaptée pour utiliser mon fichier de dictionnaire. Ma propre implémentation perl a nécessité 0,54 seconde pour s'exécuter.
C'était mon approche:
Créez un hachage de transition pour modéliser les transitions légales.
Parcourez les 16 ^ 3 combinaisons possibles de trois lettres.
Parcourez ensuite tous les mots du dictionnaire.
Imprimez les mots que j'ai trouvés.
J'ai essayé des séquences de 3 et 4 lettres, mais des séquences de 4 lettres ont ralenti le programme.
Dans mon code, j'utilise / usr / share / dict / words pour mon dictionnaire. Il est livré en standard sur MAC OS X et de nombreux systèmes Unix. Vous pouvez utiliser un autre fichier si vous le souhaitez. Pour résoudre un autre puzzle, changez simplement la variable @puzzle. Ce serait facile à adapter pour des matrices plus grandes. Il vous suffirait de modifier le hachage% transitions et le hachage% legalTransitions.
La force de cette solution est que le code est court et les structures de données simples.
Voici le code Perl (qui utilise trop de variables globales, je sais):
la source
Je sais que je suis super en retard, mais j'en ai fait un il y a quelque temps en PHP - juste pour le plaisir aussi ...
http://www.lostsockdesign.com.au/sandbox/boggle/index.php?letters=fxieamloewbxastu 75 mots (133 pts) trouvés en 0.90108 secondes.
F.........X..I..............E............... A......................................M..............................L............................O............................... E....................W............................B..........................X A..................S..................................................T.................U....
Donne une indication de ce que le programme fait réellement - chaque lettre est l'endroit où elle commence à parcourir les modèles tandis que chaque «. montre un chemin qu'il a essayé de prendre. Le plus '.' il y en a plus qu'il a cherché.
Faites-moi savoir si vous voulez le code ... c'est un horrible mélange de PHP et HTML qui n'a jamais été conçu pour voir le jour donc je n'ose pas le poster ici: P
la source
J'ai passé 3 mois à travailler sur une solution au problème des 10 meilleures cartes Boggle denses 5x5.
Le problème est maintenant résolu et présenté avec une divulgation complète sur 5 pages Web. Veuillez me contacter avec des questions.
L'algorithme d'analyse de la carte utilise une pile explicite pour parcourir de manière pseudo-récursive les carrés de la carte via un graphique de mots acyclique dirigé avec des informations directes sur l'enfant et un mécanisme de suivi de l'horodatage. Il s'agit très probablement de la structure de données de lexique la plus avancée au monde.
Le schéma évalue quelque 10 000 très bonnes cartes par seconde sur un quad core. (9500+ points)
Page Web parent:
DeepSearch.c - http://www.pathcom.com/~vadco/deep.html
Pages Web des composants:
Tableau de bord optimal - http://www.pathcom.com/~vadco/binary.html
Structure avancée du lexique - http://www.pathcom.com/~vadco/adtdawg.html
Algorithme d'analyse de carte - http://www.pathcom.com/~vadco/guns.html
Traitement par lots parallèle - http://www.pathcom.com/~vadco/parallel.html
- Cet ensemble complet de travaux n'intéressera qu'une personne qui exige le meilleur.
la source
Votre algorithme de recherche diminue-t-il continuellement la liste de mots à mesure que votre recherche se poursuit?
Par exemple, dans la recherche ci-dessus, il n'y a que 13 lettres par lesquelles vos mots peuvent commencer (ce qui réduit de moitié le nombre de lettres de départ).
Plus vous permutez de lettres, plus les ensembles de mots disponibles diminueront, ce qui réduira la recherche nécessaire.
Je commencerais par là.
la source
Je devrais réfléchir davantage à une solution complète, mais comme une optimisation pratique, je me demande si cela pourrait valoir la peine de pré-calculer un tableau de fréquences de digrammes et de trigrammes (combinaisons de 2 et 3 lettres) basé sur tous les mots de votre dictionnaire, et utilisez-le pour prioriser votre recherche. J'irais avec les premières lettres de mots. Donc, si votre dictionnaire contient les mots "Inde", "Eau", "Extrême" et "Extraordinaire", votre tableau pré-calculé pourrait être:
Recherchez ensuite ces digrammes dans l'ordre de similitude (d'abord EX, puis WA / IN)
la source
Tout d'abord, lisez comment l'un des concepteurs du langage C # a résolu un problème connexe: http://blogs.msdn.com/ericlippert/archive/2009/02/04/a-nasality-talisman-for-the-sultana-analyst.aspx .
Comme lui, vous pouvez commencer avec un dictionnaire et canoniser les mots en créant un dictionnaire à partir d'un tableau de lettres triées alphabétiquement à une liste de mots qui peuvent être épelés à partir de ces lettres.
Ensuite, commencez à créer les mots possibles à partir du tableau et à les rechercher. Je suppose que cela vous mènera assez loin, mais il y a certainement plus de trucs qui pourraient accélérer les choses.
la source
Je suggère de faire un arbre de lettres basé sur des mots. L'arbre serait composé d'une structure de lettre, comme ceci:
Ensuite, vous construisez l'arbre, chaque profondeur ajoutant une nouvelle lettre. En d'autres termes, au premier niveau, il y aurait l'alphabet; puis de chacun de ces arbres, il y aurait encore 26 autres entrées, et ainsi de suite, jusqu'à ce que vous ayez épelé tous les mots. Accrochez-vous à cet arbre analysé, et cela rendra toutes les réponses possibles plus rapides à rechercher.
Avec cet arbre analysé, vous pouvez très rapidement trouver des solutions. Voici le pseudo-code:
Cela pourrait être accéléré avec un peu de programmation dynamique. Par exemple, dans votre échantillon, les deux «A» sont à la fois à côté d'un «E» et d'un «W», qui (à partir du moment où ils les ont frappés) seraient identiques. Je n'ai pas assez de temps pour vraiment énoncer le code pour cela, mais je pense que vous pouvez rassembler l'idée.
De plus, je suis sûr que vous trouverez d'autres solutions si vous recherchez Google pour "Boggle Solver".
la source
Juste pour le plaisir, j'en ai implémenté un en bash. Ce n'est pas super rapide, mais raisonnable.
http://dev.xkyle.com/bashboggle/
la source
Hilarant. J'ai presque posté la même question il y a quelques jours à cause du même putain de jeu! Je ne l'ai pas fait cependant, car j'ai simplement recherché sur Google pour python boggle solver et obtenu toutes les réponses que je pouvais souhaiter.
la source
Je me rends compte que l'heure de cette question est venue et révolue, mais comme je travaillais moi-même sur un solveur et que je suis tombé dessus en cherchant sur Google, j'ai pensé que je devrais poster une référence à la mienne car elle semble un peu différente de certaines autres.
J'ai choisi d'aller avec un tableau plat pour le plateau de jeu et de faire des chasses récursives à partir de chaque lettre du plateau, en passant d'un voisin valide à un voisin valide, en étendant la chasse si la liste actuelle des lettres est un préfixe valide dans un index. En parcourant la notion du mot courant, il y a la liste des index dans le tableau, pas les lettres qui composent un mot. Lors de la vérification de l'index, les index sont traduits en lettres et la vérification est effectuée.
L'index est un dictionnaire de force brute qui ressemble un peu à un trie, mais permet des requêtes pythoniques de l'index. Si les mots «chat» et «traiteur» sont dans la liste, vous obtiendrez ceci dans le dictionnaire:
Donc, si le mot_courant est 'ca', vous savez que c'est un préfixe valide car
'ca' in d
renvoie True (continuez donc la traversée de la carte). Et si le mot_actuel est 'cat', alors vous savez que c'est un mot valide car c'est un préfixe valide et'cat' in d['cat']
renvoie aussi True.Si vous sentez que cela permet un code lisible qui ne semble pas trop lent. Comme tout le monde, la dépense dans ce système est de lire / construire l'index. Résoudre la planche est à peu près du bruit.
Le code se trouve à http://gist.github.com/268079 . Il est intentionnellement vertical et naïf avec beaucoup de vérification de validité explicite parce que je voulais comprendre le problème sans le rogner avec un tas de magie ou d'obscurité.
la source
J'ai écrit mon solveur en C ++. J'ai implémenté une arborescence personnalisée. Je ne suis pas sûr qu'il puisse être considéré comme un trie, mais c'est similaire. Chaque nœud a 26 branches, 1 pour chaque lettre de l'alphabet. Je traverse les branches de la planche à roulettes en parallèle avec les branches de mon dictionnaire. Si la branche n'existe pas dans le dictionnaire, j'arrête de la rechercher sur le tableau Boggle. Je convertis toutes les lettres du tableau en pouces. Donc 'A' = 0. Comme ce ne sont que des tableaux, la recherche est toujours O (1). Chaque nœud stocke s'il termine un mot et combien de mots existent dans ses enfants. L'arbre est taillé car les mots sont trouvés pour réduire la recherche répétée des mêmes mots. Je pense que l'élagage est également O (1).
Processeur: Pentium SU2700 1,3 GHz
RAM: 3 Go
Charge un dictionnaire de 178 590 mots en <1 seconde.
Résout le Boggle 100x100 (boggle.txt) en 4 secondes. ~ 44 000 mots trouvés.
Résoudre un Boggle 4x4 est trop rapide pour fournir une référence significative. :)
Résolution rapide Boggle Solver GitHub
la source
Étant donné un tableau Boggle avec N lignes et M colonnes, supposons ce qui suit:
Sous ces hypothèses, la complexité de cette solution est O (N * M).
Je pense que la comparaison des durées de fonctionnement de cet exemple de carte à bien des égards manque le point mais, par souci d'exhaustivité, cette solution se termine en <0,2 s sur mon MacBook Pro moderne.
Cette solution trouvera tous les chemins possibles pour chaque mot du corpus.
la source
Cette solution donne également la direction de recherche dans le tableau donné
Algo:
Production:
Code:
la source
J'ai implémenté une solution dans OCaml . Il précompile un dictionnaire sous forme de trie et utilise des fréquences de séquence de deux lettres pour éliminer les bords qui ne pourraient jamais apparaître dans un mot pour accélérer encore le traitement.
Il résout votre exemple de carte en 0,35 ms (avec un temps de démarrage supplémentaire de 6 ms qui est principalement lié au chargement du trie en mémoire).
Les solutions trouvées:
la source
/usr/share/dict
dictionnaire local et certains mots sont effectivement manquants (comme EMBOLE).Une solution JavaScript Node.JS. Calcule les 100 mots uniques en moins d'une seconde, ce qui comprend la lecture d'un fichier de dictionnaire (MBA 2012).
Production:
["FAM", "TUX", "TUB", "FAE", "ELI", "ELM", "ELB", "TWA", "TWA", "SAW", "AMI", "SWA" , "SWA", "AME", "SEA", "SEW", "AES", "AWL", "AWE", "SEA", "AWA", "MIX", "MIL", "AST", " ASE "," MAX "," MAE "," MAW "," MEW "," AWE "," MES "," AWL "," LIE "," LIM "," AWA "," AES "," BUT " , "BLO", "WAS", "WAE", "WEA", "LEI", "LEO", "LOB", "LOX", "WEM", "OIL", "OLM", "WEA", " WAE "," WAX "," WAF ","MILO", "EAST", "WAME", "TWAS", "TWAE", "EMIL", "WEAM", "OIME", "AXIL", "WEST", "TWAE", "LIMB", "WASE "," WAST "," BLEO "," STUB "," BOIL "," BOLE "," LIME "," SAWT "," LIMA "," MESA "," MEWL "," AXLE "," FAME ", "ASEM", "MILE", "AMIL", "SEAX", "SEAM", "SEMI", "SWAM", "AMBO", "AMLI", "AXILE", "AMBLE", "SWAMI", "AWEST "," AWEST "," LIMAX "," LIMES "," LIMBU "," LIMBO "," EMBOX "," SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]EST "," WAME "," TWAS "," TWAE "," EMIL "," WEAM "," OIME "," AXIL "," WEST "," TWAE "," LIMB "," WASE "," WAST " , "BLEO", "STUB", "BOIL", "BOLE", "LIME", "SAWT", "LIMA", "MESA", "MEWL", "AXLE", "FAME", "ASEM", " MILE "," AMIL "," SEAX "," SEAM "," SEMI "," SWAM "," AMBO "," AMLI "," AXILE "," AMBLE "," SWAMI "," AWEST "," AWEST " , "LIMAX", "LIMES", "LIMBU", "LIMBO", "EMBOX", "SEMBLE", "EMBOLE", "WAMBLE", "FAMBLE"]EST "," WAME "," TWAS "," TWAE "," EMIL "," WEAM "," OIME "," AXIL "," WEST "," TWAE "," LIMB "," WASE "," WAST " , "BLEO", "STUB", "BOIL", "BOLE", "LIME", "SAWT", "LIMA", "MESA", "MEWL", "AXLE", "FAME", "ASEM", " MILE "," AMIL "," SEAX "," SEAM "," SEMI "," SWAM "," AMBO "," AMLI "," AXILE "," AMBLE "," SWAMI "," AWEST "," AWEST " , "LIMAX", "LIMES", "LIMBU", "LIMBO", "EMBOX", "SEMBLE", "EMBOLE", "WAMBLE", "FAMBLE"]"TWAE", "EMIL", "WEAM", "OIME", "AXIL", "WEST", "TWAE", "LIMB", "WASE", "WAST", "BLEO", "STUB", "BOIL "," BOLE "," LIME "," SAWT "," LIMA "," MESA "," MEWL "," AXLE "," FAME "," ASEM "," MILE "," AMIL "," SEAX ", "SEAM", "SEMI", "SWAM", "AMBO", "AMLI", "AXILE", "AMBLE", "SWAMI", "AWEST", "AWEST", "LIMAX", "LIMES", "LIMBU "," LIMBO "," EMBOX "," SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]"TWAE", "EMIL", "WEAM", "OIME", "AXIL", "WEST", "TWAE", "LIMB", "WASE", "WAST", "BLEO", "STUB", "BOIL "," BOLE "," LIME "," SAWT "," LIMA "," MESA "," MEWL "," AXLE "," FAME "," ASEM "," MILE "," AMIL "," SEAX ", "SEAM", "SEMI", "SWAM", "AMBO", "AMLI", "AXILE", "AMBLE", "SWAMI", "AWEST", "AWEST", "LIMAX", "LIMES", "LIMBU "," LIMBO "," EMBOX "," SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]"WEST", "TWAE", "LIMB", "WASE", "WAST", "BLEO", "STUB", "BOIL", "BOLE", "LIME", "SAWT", "LIMA", "MESA "," MEWL "," AXLE "," FAME "," ASEM "," MILE "," AMIL "," SEAX "," SEAM "," SEMI "," SWAM "," AMBO "," AMLI ", "AXILE", "AMBLE", "SWAMI", "AWEST", "AWEST", "LIMAX", "LIMES", "LIMBU", "LIMBO", "EMBOX", "SEMBLE", "EMBOLE", "WAMBLE "," FAMBLE "]"WEST", "TWAE", "LIMB", "WASE", "WAST", "BLEO", "STUB", "BOIL", "BOLE", "LIME", "SAWT", "LIMA", "MESA "," MEWL "," AXLE "," FAME "," ASEM "," MILE "," AMIL "," SEAX "," SEAM "," SEMI "," SWAM "," AMBO "," AMLI ", "AXILE", "AMBLE", "SWAMI", "AWEST", "AWEST", "LIMAX", "LIMES", "LIMBU", "LIMBO", "EMBOX", "SEMBLE", "EMBOLE", "WAMBLE "," FAMBLE "]SAWT "," LIMA "," MESA "," MEWL "," AXLE "," FAME "," ASEM "," MILE "," AMIL "," SEAX "," SEAM "," SEMI "," SWAM " , "AMBO", "AMLI", "AXILE", "AMBLE", "SWAMI", "AWEST", "AWEST", "LIMAX", "LIMES", "LIMBU", "LIMBO", "EMBOX", " SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]SAWT "," LIMA "," MESA "," MEWL "," AXLE "," FAME "," ASEM "," MILE "," AMIL "," SEAX "," SEAM "," SEMI "," SWAM " , "AMBO", "AMLI", "AXILE", "AMBLE", "SWAMI", "AWEST", "AWEST", "LIMAX", "LIMES", "LIMBU", "LIMBO", "EMBOX", " SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]LIMAX "," LIMES "," LIMBU "," LIMBO "," EMBOX "," SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]LIMAX "," LIMES "," LIMBU "," LIMBO "," EMBOX "," SEMBLE "," EMBOLE "," WAMBLE "," FAMBLE "]
Code:
la source
Je voulais donc ajouter une autre façon de résoudre ce problème avec PHP, car tout le monde aime PHP. Il y a un peu de refactorisation que je voudrais faire, comme utiliser une correspondance d'expression rationnelle avec le fichier de dictionnaire, mais en ce moment je charge juste le fichier de dictionnaire entier dans une wordList.
Je l'ai fait en utilisant une idée de liste chaînée. Chaque nœud a une valeur de caractère, une valeur d'emplacement et un pointeur suivant.
La valeur d'emplacement est la façon dont j'ai découvert si deux nœuds sont connectés.
Donc, en utilisant cette grille, je sais que deux nœuds sont connectés si l'emplacement du premier nœud est égal à l'emplacement du deuxième nœud +/- 1 pour la même ligne, +/- 9, 10, 11 pour la ligne au-dessus et en dessous.
J'utilise la récursivité pour la recherche principale. Il enlève un mot de la liste de mots, trouve tous les points de départ possibles, puis trouve récursivement la prochaine connexion possible, en gardant à l'esprit qu'il ne peut pas aller à un emplacement qu'il utilise déjà (c'est pourquoi j'ajoute $ notInLoc).
Quoi qu'il en soit, je sais qu'il a besoin d'être refactorisé et j'aimerais entendre des réflexions sur la façon de le rendre plus propre, mais il produit les bons résultats en fonction du fichier de dictionnaire que j'utilise. En fonction du nombre de voyelles et de combinaisons sur la carte, cela prend environ 3 à 6 secondes. Je sais qu'une fois que j'ai preg_match les résultats du dictionnaire, cela réduira considérablement.
la source
Je sais que je suis vraiment en retard à la fête mais j'ai implémenté, comme exercice de codage, un solveur de boggle dans plusieurs langages de programmation (C ++, Java, Go, C #, Python, Ruby, JavaScript, Julia, Lua, PHP, Perl) et J'ai pensé que quelqu'un pourrait être intéressé par ceux-ci, alors je laisse le lien ici: https://github.com/AmokHuginnsson/boggle-solvers
la source
Voici la solution Utilisation de mots prédéfinis dans la boîte à outils NLTK NLTK a un package nltk.corpus en ce que nous avons un package appelé mots et il contient plus de 2 mots anglais en Lakhs que vous pouvez simplement utiliser tous dans votre programme.
Une fois que vous avez créé votre matrice, convertissez-la en un tableau de caractères et exécutez ce code
Production:
J'espère que vous l'aurez compris.
la source
Voici mon implémentation java: https://github.com/zouzhile/interview/blob/master/src/com/interview/algorithms/tree/BoggleSolver.java
La construction de Trie a pris 0 heures, 0 minutes, 1 secondes, 532 millisecondes
La recherche de mots a pris 0 heures, 0 minutes, 0 secondes, 92 millisecondes
Remarque: J'ai utilisé le dictionnaire et la matrice de caractères au début de ce fil. Le code a été exécuté sur mon MacBookPro, voici quelques informations sur la machine.
Nom du modèle: MacBook Pro
Identifiant du modèle: MacBookPro8,1
Nom du processeur: Intel Core i5
Vitesse du processeur: 2,3 GHz
Nombre de processeurs: 1
Nombre total de cœurs: 2
Cache L2 (par cœur): 256 Ko
Cache L3: 3 Mo
Mémoire: 4 GB
Boot ROM Version: MBP81.0047.B0E
SMC Version (système): 1.68f96
la source
J'ai résolu cela aussi, avec Java. Mon implémentation est longue de 269 lignes et assez facile à utiliser. Vous devez d'abord créer une nouvelle instance de la classe Boggler, puis appeler la fonction de résolution avec la grille comme paramètre. Il faut environ 100 ms pour charger le dictionnaire de 50 000 mots sur mon ordinateur et il trouve les mots en 10-20 ms environ. Les mots trouvés sont stockés dans un ArrayList,
foundWords
.la source
J'ai résolu cela en c. Il faut environ 48 ms pour fonctionner sur ma machine (avec environ 98% du temps passé à charger le dictionnaire à partir du disque et à créer le trie). Le dictionnaire est / usr / share / dict / american-english qui contient 62886 mots.
Code source
la source
J'ai résolu cela parfaitement et très rapidement. Je l'ai mis dans une application Android. Regardez la vidéo sur le lien Play Store pour la voir en action.
Word Cheats est une application qui «craque» tout jeu de mots de style matriciel. Cette application a été conçue pour m'aider à tricher avec Word Scrambler. Il peut être utilisé pour les recherches de mots, les ruptures, les mots, le moteur de recherche de mots, le crack de mots, les boggles et plus encore!
Il peut être vu ici https://play.google.com/store/apps/details?id=com.harris.wordcracker
Voir l'application en action dans la vidéo https://www.youtube.com/watch?v=DL2974WmNAI
la source