Ceci a été écrit dans le cadre du premier puzzle de programmation Premier périodique .
Le jeu
Un mot de début et de fin de même longueur est fourni. L'objectif du jeu est de changer une lettre dans le mot de départ pour former un mot valide différent, en répétant cette étape jusqu'à ce que le mot de fin soit atteint, en utilisant le moins d'étapes. Par exemple, étant donné les mots TREE et FLED, la sortie serait:
TREE
FREE
FLEE
FLED
2
Caractéristiques
- Les articles Wikipedia pour le OWL ou les SOWPODS pourraient être un point de départ utile en ce qui concerne les listes de mots.
- Le programme doit prendre en charge deux manières de sélectionner les mots de début et de fin:
- Spécifié par l'utilisateur via la ligne de commande, stdin ou tout ce qui convient à la langue de votre choix (mentionnez simplement ce que vous faites).
- Sélection de 2 mots au hasard dans le fichier.
- Les mots de début et de fin, ainsi que tous les mots intermédiaires doivent avoir la même longueur.
- Chaque étape doit être imprimée sur sa ligne.
- La dernière ligne de votre sortie doit être le nombre d'étapes intermédiaires qui ont été nécessaires pour passer entre les mots de début et de fin.
- Si aucune correspondance ne peut être trouvée entre les mots de début et de fin, la sortie doit être composée de 3 lignes: le mot de début, le mot de fin et le mot OY.
- Incluez la notation Big O pour votre solution dans votre réponse
- Veuillez inclure 10 paires de mots de début et de fin uniques (avec leur sortie, bien sûr) pour montrer les étapes produites par votre programme. (Pour économiser de l'espace, alors que votre programme devrait les afficher sur des lignes individuelles, vous pouvez les consolider en une seule ligne pour publication, en remplaçant les nouvelles lignes par des espaces et une virgule entre chaque exécution.
Objectifs / Critères gagnants
- La solution Big O la plus rapide / la meilleure produisant les étapes intermédiaires les plus courtes après une semaine sera gagnante.
- Si une égalité résulte des critères Big O, le code le plus court l'emportera.
- S'il y a toujours égalité, la première solution à atteindre sa révision la plus rapide et la plus courte l'emportera.
Tests / sortie d'échantillon
DIVE
DIME
DAME
NAME
2
PEACE
PLACE
PLATE
SLATE
2
HOUSE
HORSE
GORSE
GORGE
2
POLE
POSE
POST
PAST
FAST
3
Validation
Je travaille sur un script qui peut être utilisé pour valider la sortie.
Ce sera:
- Assurez-vous que chaque mot est valide.
- Assurez-vous que chaque mot est exactement 1 lettre différente du mot précédent.
Ça ne sera pas:
- Vérifiez que le nombre d'étapes le plus court a été utilisé.
Une fois que j'aurai écrit cela, je mettrai bien sûr à jour ce post. (:
code-challenge
word-puzzle
1p5
Rebecca Chernoff
la source
la source
HOUSE
àGORGE
soit signalée comme 2. Je me rends compte qu'il y a 2 mots intermédiaires, donc cela a du sens, mais le nombre d'opérations serait plus intuitif.The fastest/best Big O solution producing the shortest interim steps after one week will win.
comme vous ne pouvez pas garantir que la solution la plus rapide est quant à elle celle qui utilise le moins d'étapes, vous devez fournir une préférence, si une solution utilise moins d'étapes, mais atteint l'objectif plus tard.BAT
etCAT
je n'aurai aucun pas, non?Réponses:
La longueur étant répertoriée comme critère, voici la version golfée à 1681 caractères (pourrait encore être améliorée de 10%):
La version non golfée, qui utilise des noms et des méthodes de package et ne donne pas d'avertissement ou n'étend les classes que pour les alias, est:
Comme vous pouvez le voir, l'analyse des coûts de fonctionnement est
O(filelength + num_words * hash + V * n * (n + hash) + E * hash)
. Si vous acceptez mon hypothèse qu'une insertion / recherche de table de hachage est à temps constant, c'estO(filelength + V n^2 + E)
. Les statistiques particulières des graphiques dans SOWPODS signifient queO(V n^2)
cela domine vraimentO(E)
pour la plupartn
.Exemples de sorties:
IDOLA, IDOLS, IDYLS, ODYLS, ODALS, OVALS, OVELS, FOURS, EVENS, ETENS, STENS, SKENS, SKINS, SPINS, SPINE, 13
WICCA, PROSY, OY
BRINY, BRINS, TRINS, TAINS, TARNS, YARNS, YAWNS, YAWPS, YAPPS, 7
GALES, GAZ, GASTS, GESTS, GESTE, GESSE, DESSE, 5
SURES, DURES, DUNES, DINES, DINGS, DINGY, 4
LICHT, LIGHT, BIGHT, BIGOT, BIGOS, BIROS, GIROS, GIRNS, GURNS, GUANS, GUANA, RUANA, 10
SARGE, SERGE, SERRE, SERRS, SEERS, DEERS, DYERS, OYERS, OVERS, OVELS, OVALS, ODALS, ODYLS, IDYLS, 12
KEIRS, SEIRS, SEERS, BEERS, BRERS, BRERE, BREME, CREME, CREPE, 7
C'est l'une des 6 paires avec le chemin le plus long et le plus court:
GAINEST, FAINEST, FAIREST, SAIREST, SAIDEST, SADDEST, MADDEST, MIDDEST, MILDEST, WILDEST, WILIEST, WALIEST, WANIEST, CANIEST, CANTEST, CONTEST, CONFEST, CONFESS, CONFERS, CONKERS, COOKERS, COOPERS, COPPERS, POPPERS, POPPERS, POPPERS, POPPERS POPPITS, POPPIES, POPSIES, MOPSIES, MOUSIES, MOUSSES, POUSSES, PLUSSES, PLISSES, PRISSES, PRESSES, PREASES, UREASES, UNASES, UNCASES, UNASAS, UNBASED, UNBATED, UNMATED, UNMETED, UNMEWED, ENDEWED, ENDEWED INDEX, INDENES, INDENTS, INCENTS, INCESTS, INFESTS, INFECTS, INJECTS, 56
Et l'une des paires de 8 lettres solubles les plus défavorables:
ENROBING, UNROBING, UNROPING, UNCOPING, UNCAPING, UNCAGING, ENCAGING, ENRAGING, ENRACING, ENLACING, UNLACING, UNLAYING, UPLAYING, SPLAYING, SPRAYING, STRAYING, STOUMING, STOUMING, STOUMING CRIMPING, CRISPING, CRISPINS, CRISPENS, CRISPERS, CRIMPERS, CRAMPERS, CLAMPERS, CLASPERS, CLASHERS, SLASHERS, SLATHERS, SLITHERS, SMITHERS, SMOTHERS, SWOTHERS, SUDERS, MOUTHERS, MOUCHERS, COUCHERS, PACHERS, POACHERS, POACHERS, POACHERS LUNCHERS, LYNCHERS, LYNCHETS, LINCHETS, 52
Maintenant que je pense avoir éliminé toutes les exigences de la question, ma discussion.
Pour un CompSci, la question se réduit évidemment au chemin le plus court dans un graphe G dont les sommets sont des mots et dont les bords relient des mots différents dans une lettre. Générer le graphique efficacement n'est pas anodin - j'ai en fait une idée que je dois revoir pour réduire la complexité à O (V n hash + E). La façon dont je le fais consiste à créer un graphique qui insère des sommets supplémentaires (correspondant aux mots avec un caractère générique) et qui est homéomorphe au graphique en question. J'ai envisagé d'utiliser ce graphique plutôt que de le réduire à G - et je suppose que d'un point de vue golfique j'aurais dû le faire - sur la base qu'un nœud générique avec plus de 3 arêtes réduit le nombre d'arêtes dans le graphique, et le le temps d'exécution standard du pire cas des algorithmes de chemin le plus court est
O(V heap-op + E)
.Cependant, la première chose que j'ai faite a été d'exécuter des analyses des graphiques G pour différentes longueurs de mots, et j'ai découvert qu'elles sont extrêmement rares pour les mots de 5 lettres ou plus. Le graphique à 5 lettres a 12478 sommets et 40759 arêtes; l'ajout de nœuds de lien aggrave le graphique. Au moment où vous avez jusqu'à 8 lettres, il y a moins de bords que de nœuds, et 3/7 des mots sont "distants". J'ai donc rejeté cette idée d'optimisation car elle n'était pas vraiment utile.
L'idée qui s'est avérée utile était d'examiner le tas. Je peux honnêtement dire que j'ai mis en œuvre des tas modérément exotiques dans le passé, mais aucun aussi exotique que cela. J'utilise une étoile A (puisque C n'apporte aucun avantage compte tenu du tas que j'utilise) avec l'heuristique évidente du nombre de lettres différentes de la cible, et un peu d'analyse montre qu'à tout moment il n'y a pas plus de 3 priorités différentes dans le tas. Lorsque je fais apparaître un nœud dont la priorité est (coût + heuristique) et que je regarde ses voisins, je considère trois cas: 1) le coût du voisin est le coût + 1; l'heuristique du voisin est l'heuristique-1 (car la lettre qu'elle change devient "correcte"); 2) coût + 1 et heuristique + 0 (parce que la lettre qu'il change passe de "faux" à "toujours faux"; 3) coût + 1 et heuristique + 1 (car la lettre qu'il change passe de «correcte» à «fausse»). Donc, si je détends le voisin, je vais l'insérer à la même priorité, priorité + 1 ou priorité + 2. En conséquence, je peux utiliser un tableau à 3 éléments de listes liées pour le tas.
Je devrais ajouter une note sur mon hypothèse selon laquelle les recherches de hachage sont constantes. Très bien, direz-vous, mais qu'en est-il des calculs de hachage? La réponse est que je les amortis:
java.lang.String
met en cache sonhashCode()
, donc le temps total passé à calculer les hachages estO(V n^2)
(pour générer le graphique).Il y a un autre changement qui affecte la complexité, mais la question de savoir s'il s'agit d'une optimisation ou non dépend de vos hypothèses sur les statistiques. (IMO mettant "la meilleure solution Big O" comme critère est une erreur car il n'y a pas de meilleure complexité, pour une raison simple: il n'y a pas une seule variable). Cette modification affecte l'étape de génération du graphique. Dans le code ci-dessus, c'est:
Voilà
O(V * n * (n + hash) + E * hash)
. Mais laO(V * n^2)
partie vient de la génération d'une nouvelle chaîne de n caractères pour chaque lien, puis du calcul de son code de hachage. Cela peut être évité avec une classe d'assistance:Ensuite, la première moitié de la génération du graphique devient
En utilisant la structure du code de hachage, nous pouvons générer les liens dans
O(V * n)
. Cependant, cela a un effet d'entraînement. Inhérent à mon hypothèse que les recherches de hachage sont à temps constant est une hypothèse selon laquelle la comparaison des objets pour l'égalité est bon marché. Cependant, le test d'égalité de Link estO(n)
dans le pire des cas. Le pire des cas est lorsque nous avons une collision de hachage entre deux liens égaux générés à partir de mots différents - c'est-à-dire qu'elle se produitO(E)
fois dans la seconde moitié de la génération du graphe. En dehors de cela, sauf dans le cas peu probable d'une collision de hachage entre des liens non égaux, nous sommes bons. Nous avons donc échangéO(V * n^2)
pourO(E * n * hash)
. Voir mon point précédent sur les statistiques.la source
Java
Complexité : ?? (Je n'ai pas de diplôme CompSci, j'apprécierais donc de l'aide à ce sujet.)
Entrée : Fournissez une paire de mots (plus d'une paire si vous le souhaitez) dans la ligne de commande. Si aucune ligne de commande n'est spécifiée, 2 mots aléatoires distincts sont choisis.
la source
System.nanoTime()
.c sur unix
Utilisation de l'algorithme dijkstra.
Une grande partie du code est une implémentation d'arbre n-aire de costume, qui sert à maintenir
Toute personne intéressée à voir comment il fonctionne devrait probablement lire
findPath
,process
etprocessOne
(et leurs commentaires associés). Et peutbuildPath
- être etbuildPartialPath
. Le reste est la comptabilité et les échafaudages. Plusieurs routines utilisées lors des tests et du développement mais pas dans la version "production" ont été laissées en place.j'utilise
/usr/share/dict/words
sur ma boîte Mac OS 10.5, qui a tellement d'entrées ésotériques longues que le laisser fonctionner complètement au hasard génère beaucoup deOY
s.Quelques sorties:
L'analyse de complexité n'est pas anodine. La recherche est un approfondissement itératif bilatéral.
W
.S_min = (<number of different letter>-1)
dû au que si nous ne sommes séparés que par une lettre, nous notons le changement à 0 pas intermédiaire. Le maximum est difficile à quantifier voir la course TRICKY - SWOOSH ci-dessus. Chaque moitié de l'arbre seraS/2-1
àS/2
B
.Donc, le nombre minimum d'opérations est d'environ
2 * (S/2)^B * W
, pas vraiment bon.la source
O(|V|+|E|)
lieu deO(|E|+|V| log |V|)
?