Objectif
À partir d’une liste de mots de passe composés de trois mots, déchiffrez-les tous. Chaque fois que vous devinez, vous recevrez un indice dans le style de Mastermind , décrivant combien de caractères correspondent au mot de passe et combien se trouvent dans leur position correcte. L'objectif est de minimiser le nombre total de suppositions sur tous les cas de test.
Mots de passe
Dans la liste de mots par défaut de mon système, j'ai choisi au hasard 10 000 mots distincts pour constituer le dictionnaire de ce défi. Tous les mots sont composés de a-z
seulement. Ce dictionnaire peut être trouvé ici ( brut ).
À partir de ce dictionnaire, j'ai généré 1000 phrases secrètes composées de trois mots aléatoires séparés par des espaces chacun ( apple jacks fever
par exemple). Des mots individuels peuvent être réutilisés dans chaque phrase secrète ( hungry hungry hippos
). Vous pouvez trouver la liste des mots de passe ici ( brute ), avec un par ligne.
Votre programme peut utiliser / analyser le fichier de dictionnaire comme bon vous semble. Vous ne pouvez pas analyser les phrases de passe à optimiser pour cette liste spécifique. Votre algorithme devrait quand même fonctionner avec une liste de phrases différente
Devinant
Pour deviner, vous soumettez une chaîne à un vérificateur. Il ne devrait retourner que :
- Le nombre de caractères de votre chaîne également dans la phrase secrète ( pas à la bonne position)
- Le nombre de caractères dans la position correcte
Si votre chaîne est une correspondance parfaite, il peut produire quelque chose indiquant que (le mien utilise -1
pour la première valeur).
Par exemple, si la phrase secrète est the big cat
et que vous devinez tiger baby mauling
, le vérificateur devrait être renvoyé 7,1
. 7 caractères ( ige<space>ba<space>
) sont dans les deux chaînes mais dans des positions différentes, et 1 ( t
) est dans la même position dans les deux. Notez que les espaces comptent.
J'ai écrit un exemple (lu: pas optimisé) en Java, mais n'hésitez pas à écrire le vôtre tant qu'il ne donne que les informations requises.
int[] guess(String in){
int chars=0, positions=0;
String pw = currentPassword; // set elsewhere, contains current pass
for(int i=0;i<in.length()&&i<pw.length();i++){
if(in.charAt(i)==pw.charAt(i))
positions++;
}
if(positions == pw.length() && pw.length()==in.length())
return new int[]{-1,positions};
for(int i=0;i<in.length();i++){
String c = String.valueOf(in.charAt(i));
if(pw.contains(c)){
pw = pw.replaceFirst(c, "");
chars++;
}
}
chars -= positions;
return new int[]{chars,positions};
}
Notation
Votre score est simplement le nombre de suppositions que vous soumettez au vérificateur (en comptant la dernière, la bonne) pour toutes les phrases de test. Le score le plus bas gagne.
Vous devez déchiffrer toutes les phrases de la liste. Si votre programme échoue sur l'un d'entre eux, il est invalide.
Votre programme doit être déterministe. Si exécuté deux fois sur le même ensemble de phrases secrètes, il devrait renvoyer le même résultat.
En cas d'égalité pour la première fois, je vais exécuter les entrées à égalité sur mon ordinateur quatre fois chacune, et le temps moyen le plus bas pour résoudre les 1000 cas l'emporte. Mon ordinateur fonctionne sous Ubuntu 14.04, avec un i7-3770K et 16 Go de mémoire vive, au cas où cela ferait une différence pour votre programme. Pour cette raison, et pour faciliter les tests, votre réponse doit être dans une langue avec un compilateur / interprète téléchargeable gratuitement à partir du Web (à l'exception des essais gratuits) et ne nécessitant pas d'inscription.
Titre adapté de XKCD
la source
9 0
. Cela pourrait prendre un certain temps: PRéponses:
Scala 9146 (min 7, max 15, moyenne 9.15) durée: 2000 secondes
Comme beaucoup d'entrées, je commence par obtenir la longueur totale, puis les espaces, un peu plus d'informations, la réduction aux candidats restants et des devinettes.
Inspiré par la bande dessinée originale xkcd, j'ai essayé d'appliquer ma compréhension rudimentaire de la théorie de l'information. Il y a un billion de phrases possibles ou un peu moins de 40 bits d'entropie. Je me suis fixé comme objectif moins de 10 suppositions par phrase de test, ce qui signifie que nous devons apprendre en moyenne près de 5 bits par requête (car la dernière est inutile). À chaque conjecture, nous obtenons deux nombres et, grosso modo, plus l'éventail de ces nombres est grand, plus nous nous attendons à en apprendre.
Pour simplifier la logique, j’utilise chaque requête comme deux questions distinctes. Chaque chaîne de devinettes est donc composée de deux parties: un côté gauche s'intéresse au nombre de positions correctes (chevilles noires dans le cerveau) et un côté droit s’intéresse au nombre de caractères corrects ( total des piquets). Voici un jeu typique:
Espaces de devinettes
Chaque estimation d'espace peut renvoyer au plus 2 piquets noirs; J'ai essayé de construire des hypothèses pour renvoyer 0,1 et 2 chevilles avec des probabilités 1 / 4,1 / 2 et 1/4 respectivement. Je pense que c’est le mieux que vous puissiez faire pour un volume d’information attendu de 1,5 bit. J'ai opté pour une chaîne en alternance pour la première estimation, suivie de celles générées aléatoirement, bien qu'il soit généralement intéressant de commencer à deviner dès la deuxième ou la troisième tentative, car nous connaissons les fréquences de longueur des mots.
L'apprentissage des jeux de caractères compte
Pour les suppositions du côté droit, je choisis des ensembles de caractères aléatoires (toujours 2 de e / i / a / s) afin que le nombre attendu renvoyé soit égal à la moitié de la longueur de la phrase. Une variance plus élevée signifie plus d'informations et d'après la page wikipedia sur la distribution binomiale, je calcule environ 3,5 bits par requête (au moins pour les premiers avant que les informations ne deviennent redondantes). Une fois l'espacement connu, j'utilise des chaînes aléatoires des lettres les plus courantes sur le côté gauche, choisies de manière à ne pas entrer en conflit avec le côté droit.
Coalescence des candidats restants
Ce jeu est un compromis entre vitesse de traitement / efficacité de la requête et l'énumération des candidats restants peut prendre très longtemps sans informations structurées telles que des caractères spécifiques. J'ai optimisé cette partie en recueillant principalement des informations invariantes dans l'ordre des mots, ce qui me permet de pré-calculer les comptes de jeux de caractères pour chaque mot et de les comparer avec les comptes tirés des requêtes. Je compresse ces nombres dans un entier long, en utilisant le comparateur d'égalité de la machine et l'additionneur pour tester tous mes nombres de caractères en parallèle. C'était une victoire énorme. Je peux emballer jusqu'à 9 chefs d'accusation dans le Long, mais j'ai trouvé que la collecte des informations supplémentaires n'en valait pas la peine et j'ai opté pour 6 à 7.
Une fois que les candidats restants sont connus, si l'ensemble est raisonnablement petit, je sélectionne celui dont le journal prévu est le plus faible des candidats restants. Si le jeu est assez grand pour que cela prenne beaucoup de temps, je choisis un petit jeu d'échantillons.
Merci tout le monde. C'était un jeu amusant et m'a incité à m'inscrire sur le site.
Mise à jour: code épuré pour plus de simplicité et de lisibilité, avec des ajustements mineurs à l’algorithme, ce qui a permis d’améliorer le score.
Score original: 9447 (min 7, max 13, moyenne 9,45) temps: 1876 secondes
Le nouveau code est 278 lignes de Scala, ci-dessous
la source
C - total: 37171, min: 24, max: 55, temps: environ 10 secondes
J'ai emprunté l'idée de Ray de trouver la longueur de chaque mot en devinant avec des espaces, sauf que je fais une recherche binaire plutôt que linéaire, ce qui me permet d'économiser beaucoup de suppositions.
Une fois que je détermine la longueur d'un mot, j'imagine que le premier mot correspond à sa longueur et j'enregistre le nombre de positions correctes. Ensuite, je sélectionne le premier mot de l'ensemble des mots qui partagent le même nombre de positions que le mot mystère. Pour ma troisième conjecture, je sélectionne le premier mot de l'ensemble des mots partageant le même nombre de positions que mon mot mystère et le même nombre de positions que ma deuxième conjecture avec le mot mystère, etc.
En utilisant cette méthode, je suis capable de deviner chaque mot, un à la fois, en environ 5 à 10 suppositions. Évidemment le troisième mot que je dois faire un peu différemment parce que je ne connais pas sa longueur, mais la méthode est similaire. J'utilise un fichier contenant une matrice du nombre de positions que chaque mot partage en commun et que j'ai précalculées. La majeure partie de l'exécution est engagée lors du chargement des données précalculées. Vous pouvez tout télécharger ici .
C'est aussi amusant de le regarder en mots:
la source
Python 3.4 - min: 21, max: 29, total: 25146, temps: 20min
min: 30, max: 235, total: 41636, temps: 4minMise à jour:
Cette méthode n'utilise pas le hasard pour que le score ne change pas.
D'abord, il utilise des suppositions comme celle-ci pour trouver les premier et deuxième espaces de la réponse.
Ensuite, chaque lettre est comptée en devinant
aaaaa...
,bbbbb....
... Après cela, il vous en coûtera environ 40 étapes. En fait, nous n'avons pas besoin d'essayer toutes les lettres et nous pouvons les essayer dans un ordre arbitraire. Dans la plupart des cas, essayer environ 20 lettres suffit. Ici j'ai choisi 21.Maintenant, il connaît la longueur du premier mot et du deuxième mot afin de filtrer une liste de candidats pour ces deux mots. Normalement, il y aura environ 100 candidats pour chacun.
Ensuite, il suffit d’énumérer le premier et le deuxième mot. Une fois les deux premiers mots énumérés, nous pouvons déduire tous les troisièmes mots valides puisque nous savons que le nombre de caractères est important.
Pour optimiser la vitesse, j'utilise le
concurrent.futures
pour ajouter le multitraitement au programme. Vous avez donc besoin de Python 3 pour l’exécuter et je l’ai testé avec Python 3.4 sur ma machine Linux. En outre, vous devez installernumpy
.la source
Java 13 923 (min: 11, max: 17)
Mise à jour: score amélioré (dépassé le <14 / crack moyen!), Nouveau code
Original post
J'ai décidé de me concentrer complètement sur le nombre de suppositions plutôt que sur les performances (en fonction des règles). Cela a abouti à un très programme intelligent lent.
Au lieu de voler des programmes connus, j'ai décidé de tout écrire à partir de zéro, mais il s'avère que certaines / la plupart des idées sont les mêmes.
Algorithme
Voici comment fonctionne le mien:
Exemple de suppositions
Voici un exemple concret:
Parce que mon code ne se concentre jamais sur des mots simples et ne collecte que des informations sur la phrase complète dont il a besoin pour générer beaucoup de phrases, ce qui le rend très lent.
Code
Et enfin voici le code (laid), n'essayez même pas de le comprendre, désolé:
la source
Java -
18 708 requêtes; 2,4 secondes11 077 requêtes; 125 minMin: 8, Max: 13, Requêtes effectives: 10,095
J'ai passé beaucoup trop de temps là-dessus. : P
Le code est disponible sur http://pastebin.com/7n9a50NMRev 1. disponible à http://pastebin.com/PSXU2bgaRev 2. disponible sur http://pastebin.com/gRJjpbbu
Ma deuxième révision. J'espérais pouvoir franchir la barrière des 11K pour remporter le prix, mais je n'ai plus le temps d'optimiser cette bête.
Il fonctionne sur un principe totalement distinct des deux versions précédentes (et dure environ 3 500 fois plus longtemps). Le principe général consiste à utiliser un tamisage de l'espace et des caractères pairs / impairs pour réduire la liste des candidats à une taille gérable (généralement entre 2 et 8 millions), puis d'effectuer des requêtes répétées avec un pouvoir discriminant maximal (c'est-à-dire dont la distribution de sortie a maximisé l'entropie).
Pas la vitesse mais la mémoire est la principale limitation. Ma machine virtuelle Java ne me permet pas de réserver un tas supérieur à 1 200 Mo pour une raison obscure (probablement Windows 7), et j'ai ajusté les paramètres pour me donner la meilleure solution possible qui n'épuise pas cette limite. Cela me contrarie qu'une bonne exécution avec les paramètres appropriés briserait 11K sans augmentation significative du temps d'exécution. J'ai besoin d'un nouvel ordinateur. : P
Ce qui me contrarie tout autant, c’est que 982 des requêtes de cette implémentation sont des requêtes de "validation" inutiles. Ils n'ont d'autre objectif que de satisfaire à la règle selon laquelle l'oracle doit renvoyer une valeur spéciale "vous l'avez eue" à un moment donné, même si, dans mon implémentation, la réponse correcte a été déduite avec certitude avant cette requête dans 98,2% des cas. La plupart des autres soumissions inférieures à 11K reposent sur des techniques de filtrage qui utilisent des chaînes candidates comme chaînes de requête et ne subissent donc pas les mêmes pénalités.
Pour cette raison, bien que mon nombre de requêtes officiel soit de 11 077 (à l'exception des leaders, à condition que leur code soit conforme, conforme aux spécifications, etc.), j'affirme hardiment que mon code génère 10 095 requêtes effectives , ce qui signifie que seules 10 095 requêtes sont exécutées. réellement nécessaire pour déterminer toutes les phrases de passe avec une certitude de 100%. Je ne suis pas sûr qu'aucune des autres implémentations ne corresponde à cela, c'est pourquoi je considérerai cela comme une victoire minime. ;)
la source
.
."perpetually exhausting pool"
Java - min: 22, max: 41, total: 28353, temps: 4 secondes
Le programme devine le mot de passe en 3 étapes:
Il gère également un ensemble de "caractères incorrects" renvoyant un résultat nul dans la recherche, ainsi qu'un ensemble de "caractères corrects" placés ailleurs dans la phrase secrète.
Ci-dessous un exemple des valeurs envoyées successivement pour deviner, vous pouvez voir les 3 étapes:
Le code:
la source
PYTHON 2.7 - 156821 devine, 0.6 seconde
Je me suis tourné vers la vitesse plutôt que vers le plus petit nombre de suppositions, bien que je sache que mon nombre de suppositions est toujours inférieur à ce que serait, par exemple, une attaque directe par dictionnaire. Je ne calcule pas le nombre de lettres dans le mot de passe, mais au mauvais endroit, car ma méthode ne l'utilise pas, mais si vous estimez que cela me donne un avantage injuste, je le mettrai en œuvre. Je commence simplement par une chaîne de proposition vide et j'ajoute un suffixe de caractère qui incrémente ma liste de caractères, en vérifiant le résultat de 'check' pour voir si le nombre de caractères corrects est égal à la longueur de la proposition. Par exemple, si le mot de passe était «mauvais», je suppose que:
un B
une
a B c d
J'ai également essayé de trier les lettres par fréquence de lettres anglaises, ce qui réduisait environ 35% du nombre de suppositions, ainsi que le temps. J'ai craqué tous les mots de passe en 0,82 secondes. Les statistiques sont imprimées à la fin.
EDIT: Suppression des +1 et -1 perdus de deux des boucles while des itérations précédentes de tests, ainsi que de nouvelles statistiques pour les suppositions les plus faibles et les suppositions pour un mot de passe individuel.
EDIT2: ajout d'une table de recherche pour la lettre suivante la plus courante, par lettre. Augmentation considérable de la vitesse et diminution du nombre de devinettes
la source
C ++ -
1138310989 correspondances!Mise à jour
Correction de fuites de mémoire et suppression d'une nouvelle tentative de réduction de la taille des dictionnaires de mots individuels. Prend environ 50 minutes sur mon mac pro. Le code mis à jour est sur github.
Je suis passé à la stratégie de correspondance de phrase, j'ai retravaillé le code et l'ai mis à jour sur github https://github.com/snjyjn/mastermind
Avec la correspondance basée sur la phrase, il ne reste plus que 11383 tentatives! C'est cher en terme de calcul! Je n'aime pas non plus la structure du code! Et il est toujours loin derrière les autres :-(
Voici comment je le fais:
En parallèle, ajoutez des chaînes de test 'spécialement conçues' pour obtenir plus d'informations sur la phrase. La stratégie actuelle est la suivante:
une. Utilisez les caractères dans l'ordre de leur fréquence dans le dictionnaire.
b. Nous connaissons déjà le compte pour le plus fréquent
c. 1ère chaîne de test = 5 caractères suivants. Cela nous donne le nombre de ces caractères dans la phrase.
ré. 3 chaînes de test suivantes = 5 caractères chacune, couvrant un total de 20 caractères en 4 tentatives en plus du 1 premier caractère. Cela nous donne également le compte de ces 5 derniers personnages. les ensembles avec 0 nombre sont parfaits pour réduire la taille du dictionnaire!
e. Passons maintenant au test précédent qui a eu le moins de comptes nuls, décomposez la chaîne en 2 et utilisez 1 pour le test. Le compte résultant nous parle également de l’autre division.
F. Maintenant, répétez les tests avec des caractères (base 0),
Une fois les espaces identifiés, utilisez les contraintes jusqu’à présent (autant de tests qu’il est possible de réaliser dans ces tentatives) pour réduire la taille du dictionnaire. Créez également 3 dictionnaires secondaires, 1 pour chaque mot.
Maintenant, essayez de deviner chaque mot et testez-le.
Utilisez ces résultats pour réduire les tailles de dictionnaire individuelles.
Décorez-le également avec des caractères de test (après la longueur) pour obtenir plus de contraintes sur la phrase! J'ai utilisé 3 suppositions dans la version finale - 2 pour le mot 1 et 1 pour le mot 2
Cela amène le dictionnaire à une taille gérable. Effectuez un produit croisé en appliquant toutes les contraintes comme précédemment pour créer un dictionnaire de phrases.
Résoudre pour le dictionnaire de phrases à travers une série de suppositions - cette fois en utilisant les informations de position et de correspondance de caractères.
Cette approche nous amène à 11383 tentatives:
Post précédent
J'ai nettoyé le code et l'ai téléchargé sur https://github.com/snjyjn/mastermind Au cours du processus, je l'ai amélioré et j'ai encore une idée à essayer. Il y a une différence majeure par rapport à ce que j'avais fait hier:
Les statistiques ressemblent maintenant à:
Poste originale
Toutes mes excuses pour la "réponse", mais je viens de créer un compte et je n'ai pas assez de réputation pour ajouter un commentaire.
J'ai un programme c ++, qui prend environ 6,5 secondes et 24107 tentatives de correspondance. Il s'agit d'environ 1400 lignes de c ++. Je ne suis pas content de la qualité du code et je vais le nettoyer avant de le mettre en place dans un jour ou deux. Mais dans l’intérêt de la communauté et contribuant à la discussion, voici ce que je fais:
Lisez le dictionnaire, obtenez des informations de base à son sujet - longueur de mot minimale / maximale, fréquence des caractères, etc.
Identifiez d'abord les espaces - Celui-ci comporte 2 moitiés, la première est un ensemble de requêtes qui continuent à partitionner l'espace (similaire à un C. Chafouin):
Ce n’est pas tout à fait exact, car j’utilise la longueur de mot min / max, et j’utilise le nombre de correspondances à chaque étape, mais vous avez l’idée. À ce stade, il n’ya toujours pas assez d’informations pour obtenir les 2 espaces, mais j’en ai assez pour les réduire à un petit nombre de combinaisons. À partir de ces combinaisons, je peux faire quelques requêtes spécifiques, ce qui réduira le nombre à une combinaison.
Premier mot - Obtenez un sous-dictionnaire, qui contient des mots de la bonne longueur. Le sous-dictionnaire a ses propres statistiques. Faites quelques suppositions avec les caractères les plus fréquents pour obtenir un décompte de ces caractères dans le mot. Réduisez à nouveau le dictionnaire en fonction de cette information. Créez un mot qui suppose les caractères les plus différents et utilisez-le. Chaque réponse entraîne une réduction dans le dictionnaire jusqu'à ce que nous ayons une correspondance exacte ou que le dictionnaire ait la taille 1.
Deuxième mot - semblable au premier mot
Troisième mot - c'est le plus différent des deux autres. Nous n'avons pas d'informations sur la taille, mais nous avons toutes les requêtes précédentes (que nous avons conservées). Ces requêtes vous permettent de réduire le dictionnaire. La logique est sur les lignes de:
Utilisez le dictionnaire réduit pour deviner, avec les caractères les plus divers, et continuez à réduire le dictionnaire à la taille 1 (comme dans les mots 1 et 2).
Les statistiques ressemblent à:
la source
Go - Total: 29546
Semblable à d'autres, avec quelques optimisations.
AAAAAAAABBBBBBBBCCCCCCCC...ZZZZZZZZ
Ce n'est pas particulièrement rapide.
la source
passphases
etallWords
n'est pas défini.Java: 58 233
(programme de référence)
Un simple bot à battre pour tout le monde. Il utilise 26 suppositions initiales pour chaque phrase pour établir le nombre de caractères. Ensuite, il élimine tous les mots contenant des lettres non trouvées dans la phrase.
Vient ensuite une boucle O (n 3 ) massive sur les mots restants. Il vérifie d’abord chaque phrase candidate pour voir s’il s’agit d’un anagramme. Si c'est le cas, il le devinera, ignorant les résultats, à moins que ce ne soit une correspondance parfaite. Je l'ai vu utiliser entre 28 et 510 suppositions pour une phrase donnée jusqu'à présent.
C’est lent et tout dépend du nombre de mots pouvant être éliminés directement à partir des 26 suppositions initiales. La plupart du temps, il laisse entre 1000 et 4000 mots à boucler. Pour le moment, cela dure environ 14 heures, à un rythme de ~ 180 secondes / phrase. J’estime que cela prendra 50 heures et que le score sera mis à jour à ce moment-là. Vous devriez probablement faire quelque chose de plus intelligent ou de plus filant que cela.
(mise à jour) Il a finalement fini, avec un peu moins de 60k devins.
la source
Java:
28 34026 185Min 15, Max 35, Temps 2.5s
Depuis que mon stupide bot a finalement fini de fonctionner, je voulais soumettre quelque chose un peu plus rapidement. Il fonctionne en quelques secondes, mais obtient un bon score (pas tout à fait gagnant> <).
Tout d'abord, il utilise une grosse chaîne de touches pour obtenir la longueur totale de la phrase. Puis recherche binaire pour trouver des espaces, semblables aux autres. Ce faisant, il commence également à vérifier les lettres une par une (dans l’ordre du pivot) afin d’éliminer les mots qui contiennent plus de lettres que la phrase entière.
Une fois qu'il a la longueur des mots, il utilise une étape de réduction binaire pour affiner le choix des listes de mots. Il choisit la liste la plus longue et une lettre dans environ la moitié des mots. Il devine un bloc de mots de cette lettre pour déterminer la moitié à jeter. Il utilise également les résultats pour supprimer les mots des autres listes contenant trop de lettres.
Une fois qu'une liste ne contient que des anagrammes, cela ne fonctionne pas. À ce stade, je les ai parcourues en boucle jusqu'à ce qu'il ne reste que deux (ou un si les autres mots ne sont pas connus).
Si j'ai un nombre total de mots de quatre (deux connues et une avec deux options), je passe les vérifications de réduction et d'anagramme et je devine l'une des options comme une phrase complète. Si cela ne fonctionne pas, alors il faut que ce soit l'autre, mais j'économise 50% du temps.
Voici un exemple montrant la première phrase en train d'être fissurée:
Et bien sûr, le code:
la source
C # - 10649 (min 8, max 14, moyenne: 10.6) durée: ~ 12 heures
Voici à quoi ça ressemble:
Solveur
Il utilise un solveur prospectif. Avant de deviner, il estime le nombre de valeurs distinctes renvoyées par le cerveau, compte tenu des phrases de passe actuellement possibles. La supposition qui maximise le nombre de résultats distincts est celle utilisée.
Pour la phase de détermination de l'espace, seules les combinaisons possibles de "" et "." Sont envisagées. Pour la phase de définition des phrases, il crée la liste complète des phrases secrètes actuellement possibles (ce qui explique pourquoi il est si lent).
Lettre compte
Le décompte des lettres est ajouté à la recherche d'espace. Les séries de lettres ont été choisies par une recherche avide, en ajoutant une lettre à la fois et en échantillonnant des phrases de test aléatoires pour vérifier l'efficacité de la série.
Le code est ici: https://github.com/Tyler-Gelvin/MastermindContest
Aucune interface n'étant spécifiée, toutes les entrées sont codées en dur et les tests unitaires constituent la seule interface. Le test "principal" est SolverFixture.SolveParallelAll.
la source
Main
fonction dans votre code. At-il un?SolverFixture.SolveSerialAll
correspond à ce que j’ai utilisé pour obtenir les résultats du test affichés ci-dessus etSolver.Solve
constitue le cœur du programme. C'est un projet de test unitaire sans point d'entrée officiel, donc aucunemain
fonction.C # - Total: 1000, Durée: 305 secondes, Moyenne: 24, Min: 14, Max: 32
Wow Avg <15 c'est assez bon, eh bien je ne peux pas battre ça mais j'ai essayé, alors voici mon approche. Je l'ai cassé mot par mot, puis je les ai résolus successivement. En déterminant la longueur des deux premiers mots et en faisant ensuite quelques suppositions stratégiques (filtrant chaque fois par le mot précédemment deviné), j'ai pu obtenir la réponse avec un nombre de conjectures relativement réduit. Au cours de la période de développement, j’ai pu optimiser la plupart des éléments afin de les préformer efficacement (nombre de suppositions), mais c’est le défaut de la décision de conception initiale de résoudre logiquement un mot à la fois; devine et / ou ne lance pas devine le plus efficacement possible, ce qui veut dire que je ne gagne pas celui-là; (.
Encore une conception intéressante (du moins, je le pense), une chose à noter avec le code inclus, dans certains cas, je peux déterminer la réponse sans même exécuter une supposition qui renvoie -1, si cela est nécessaire, décommentez simplement la ligne de code libellée "AJOUTER GUESS ICI (si nécessaire)" (et ajouter jusqu'à +1 à tous mes scores :()
Algorithme (Ma pensée code Sudo)
Donc, vraiment, il y a deux parties à cela, les deux premiers mots et le dernier mot. Cela n'a aucun sens pour personne d'autre que moi, mais j'ai essayé d'ajouter suffisamment de commentaires au code pour que cela ait plus de sens:
NextWord (l'un des deux premiers mots)
{
var lengthOfPossibleWord = Déterminer la longueur du mot (En code, voir: moyen efficace de trouver la longueur)
Liste des possibilités = Tous les mots de cette longueur (lengthOfPossibleWord)
Faire une proposition
possibilités = possibilités où (pour toutes les suppositions) {le nombre de caractères dans la même position est égal au mot possible
(si outOfPlace caractères est égal à 0) alors où tous les caractères sont différents du mot possible}
}
LastWord (après la résolution des deux premiers)
{
Liste des possibilités = Tous les mots filtrés par le nombre de caractères offPosition dans le second mot (En code, voir: helperWords)
Faire une proposition
possibilités = possibilités où (pour toutes les suppositions) {
Le nombre de caractères dans la même position est égal au mot possible
Somme des caractères in et out of position == mot possible (pour toutes les suppositions)
La longueur est égale ou supérieure à (somme des caractères insérés ou non), longueur du mot possible
(si outOfPlace caractères est égal à 0) alors où tous les caractères sont différents du mot possible
}
}
Code
Remarque: pour que cela fonctionne, vous devez inclure les fichiers ppcg_mastermind_dict.txt et ppcg_mastermind_passes.txt dans le répertoire en cours d'exécution (ou dans le VS dans le même répertoire et définissez "Copier dans le répertoire de sortie" sur true). Je m'excuse vraiment pour la qualité du code, mais il reste encore beaucoup de travail à faire, cela devrait marcher.
la source
Python - min: 87, max: 108, total: 96063, temps: 4s
C'est mon deuxième poste. Cette méthode utilise moins de temps mais son score est pire. Et il peut être exécuté en utilisant soit:
Pas:
. ....
,.. ...
...Cela coûte environ 90 suppositions pour chaque mot de passe.
la source
Perl (toujours en cours ... à partir de maintenant min / moy / max de 8 / 9,2 / 11, estimez-le à 1
500300 heures de temps d'exécution total)Mise à jour: modification des suppositions initiales pour l'accélérer quelque peu. Correction d'un bug.
Ce n'est probablement pas fini avant ce concours, mais je pourrais aussi le poster. Il ne détermine pas la longueur des mots, il doit donc vérifier le dictionnaire en entier, ce qui ... prend un certain temps.
Avec les deux premières suppositions, il détermine la longueur totale, le nombre de 'e' et le nombre de caractères différents qu'il contient.
Ensuite, il essaie toutes les combinaisons qui suffisent à ces statistiques ainsi que toutes les suppositions précédentes.
Cette version (et dernière) récente a ajouté mp et fonctionne actuellement sur un système à 24 cœurs.
la source
Java 10.026 (en 2.5 heures)
Voici mon code optimisé, maintenant multithread pour améliorer la vitesse:
la source