Voici un fichier texte de 1.2Mb ASCII contenant le texte de Moby-Dick de Herman Melville ; ou la baleine . Votre tâche consiste à écrire un programme ou une fonction (ou une classe, etc. - voir ci-dessous) qui recevra ce fichier caractère par caractère et à chaque étape, il devra deviner le caractère suivant.
C'est un défi de code . Votre score sera
2*L + E
où L
est la taille de votre soumission en octets et E
le nombre de caractères qu’il devine de manière incorrecte. Le score le plus bas gagne.
Autres détails
Votre soumission sera un programme ou une fonction (etc.) qui sera appelée ou invoquée ou des données envoyées plusieurs fois. (1215235 fois pour être exact.) Quand il sera appelé pour la n- ème fois, il recevra le n- ème caractère de whale.txt
ou whale2.txt
et devra deviner pour le ( n + 1 ) ème caractère. La E
composante de sa partition sera le nombre total de caractères qu'il devine de manière incorrecte.
La plupart des soumissions devront stocker un état entre les invocations, afin de pouvoir suivre le nombre de fois où elles ont été appelées et les entrées précédentes. Vous pouvez le faire en écrivant dans un fichier externe, en utilisant static
des variables globales, en soumettant une classe plutôt qu’une fonction, en utilisant une monade d’état ou tout ce qui fonctionne pour votre langue. Votre soumission doit inclure tout code nécessaire pour initialiser son état avant la première invocation.
Votre programme doit fonctionner de manière déterministe, de sorte qu'il émette toujours les mêmes suppositions avec la même entrée (et obtienne donc toujours le même score).
Votre réponse doit inclure non seulement votre soumission, mais également le code que vous avez utilisé pour calculer la E
partie de son score. Cela ne doit pas nécessairement être écrit dans la même langue que votre soumission et ne sera pas compté dans son décompte en octets. Vous êtes encouragé à le rendre lisible.
En ce qui concerne l'interface entre votre soumission et ce programme de calcul du score, tout va bien, à condition que votre programme donne toujours un octet de sortie avant de recevoir son prochain octet d'entrée. (Ainsi, par exemple, vous ne pouvez pas simplement lui transmettre une chaîne contenant toutes les entrées et obtenir une chaîne contenant toutes les sorties.)
Vous devez réellement exécuter votre programme de test et calculer / vérifier votre score avant de soumettre votre candidature. Si votre soumission est trop lente pour que vous puissiez vérifier son score, elle n'est pas qualifiée pour concurrencer, même si vous savez quel est son score en principe.
La L
composante de votre score sera calculée selon les règles habituelles pour les défis de golf de code. Si votre soumission doit contenir plusieurs fichiers, veuillez noter les règles relatives à l' évaluation et à la structure des répertoires dans ce cas. Toutes les données utilisées par votre code doivent être incluses dans votre L
score.
Vous pouvez importer des bibliothèques existantes mais ne pas charger d’autres fichiers externes et votre code peut ne pas accéder au whale.txt
ouwhale2.txt
déposer de toute autre manière que celle décrite ci-dessus. Vous ne pouvez pas charger de réseaux neuronaux pré-formés ni d'autres sources de données statistiques. (C'est bien d'utiliser des réseaux de neurones, mais vous devez inclure les données de poids dans votre soumission et les compter dans votre nombre d'octets.) Si, pour une raison quelconque, votre langue ou vos bibliothèques incluent une fonctionnalité qui fournit tout ou partie du texte de Moby Dick , vous ne pouvez pas utiliser cette fonctionnalité. En dehors de cela, vous pouvez utiliser toute autre fonctionnalité intégrée ou de bibliothèque que vous aimez, y compris celles relatives au traitement, à la prédiction ou à la compression de texte, à condition qu'elles fassent partie de votre langage ou de ses bibliothèques standard. Pour des routines plus exotiques et spécialisées incluant des sources de données statistiques, vous devez les implémenter vous-même et les inclure dans votre nombre d'octets.
Il est probable que certaines soumissions incluront des composants générés par le code. Si tel est le cas, veuillez inclure dans votre réponse le code qui a été utilisé pour les produire et expliquer comment cela fonctionne . (Tant que ce code n'est pas nécessaire pour exécuter votre soumission, il ne sera pas inclus dans votre nombre d'octets.)
Pour des raisons historiques, il existe deux versions du fichier et vous pouvez utiliser l’une ou l’autre dans une réponse. Dans whale2.txt
(lien ci-dessus), le texte n'est pas encapsulé, de sorte que les nouvelles lignes apparaissent uniquement à la fin des paragraphes. Dans l'original, whale.txt
le texte est entouré d'une largeur de 74 caractères. Vous devez donc prévoir la fin de chaque ligne ainsi que le texte. Cela rend le défi plus délicat, il whale2.txt
est donc recommandé pour de nouvelles réponses. Les deux fichiers ont la même taille, 1215236 octets.
Pour résumer, toutes les réponses devraient inclure les éléments suivants:
- Votre soumission elle-même. (Le code, ainsi que tous les fichiers de données qu'il utilise - il peut s'agir de liens s'ils sont volumineux.)
- Une explication du fonctionnement de votre code. Veuillez expliquer la méthode d’E / S ainsi que la manière dont elle prédit le caractère suivant. L'explication de votre algorithme est importante et de bonnes explications vous rapporteront des primes.
- Le code que vous avez utilisé pour évaluer votre score. (Si cela est identique à une réponse précédente, vous pouvez simplement créer un lien vers celle-ci.)
- Tout code que vous avez utilisé pour générer votre soumission, avec une explication de ce code. Cela inclut le code que vous avez utilisé pour optimiser les paramètres, générer des fichiers de données, etc. (Ceci ne compte pas dans votre nombre d'octets mais doit être inclus dans votre réponse.)
Classement
Primes
De temps en temps, je proposerai des primes pour encourager différentes approches.
Le premier, 50 points, a été attribué à A. Rex pour la réponse la plus performante à l’époque.
Le deuxième, 100 points, a également été attribué à A. Rex, pour la même réponse, car ils ont ajouté une très bonne explication à leur réponse existante.
La prochaine prime, 200 points , sera attribuée soit à
- Une réponse compétitive qui utilise une nouvelle technique. (Cela sera basé sur mon jugement subjectif puisque c'est mon représentant qui entre dans la prime, mais vous pouvez vous fier à moi pour être juste. Notez que votre réponse doit contenir suffisamment d'explications pour que je puisse comprendre comment cela fonctionne!) Ne prenez pas le score le plus élevé, il faut qu’il réussisse raisonnablement par rapport aux réponses existantes. Je suis particulièrement désireux de voir des solutions basées sur des réseaux de neurones récurrents, mais je vais attribuer la prime à tout ce qui semble assez différent des modèles de Markov qui dominent les meilleurs scores actuels.
Ou:
- Toute autre personne qui bat le meilleur score de A. Rex (actuellement 444444), quelle que soit la méthode employée.
Une fois la prime de 200 points réclamée, je proposerai très probablement une prime de 400 points, mettant à jour les exigences en conséquence.
la source
Réponses:
/// , 2 * 1 + 1020874 = 1020876
Imprime un espace.
la source
Node.js, 2 * 224 + 524279 = 524727
Veuillez vous reporter au journal des modifications à la fin de cet article pour connaître les mises à jour des scores.
Une fonction prenant et retournant un octet.
Il consiste en un modèle PPM simple qui examine les 8 derniers caractères pour prédire le prochain.
Nous faisons confiance à un modèle de longueur L lorsque nous l'avons rencontré au moins T [L] fois, où T est un tableau de seuils arbitraires: [1,1,2,1,2,3,5,2] . De plus, nous faisons toujours confiance à un motif dont le premier caractère correspond
[A-Z '"(]
.Nous sélectionnons le modèle de confiance le plus long et renvoyons la prédiction avec le score le plus élevé associé à ce modèle au moment de l'appel.
Remarques
Ce n'est évidemment pas optimisé pour la vitesse, mais cela dure environ 15 secondes sur mon ordinateur portable.
Si nous pouvions répéter le processus plusieurs fois de suite sans réinitialiser le modèle, le nombre d'erreurs convergerait vers ~ 268000 après 5 itérations.
Le taux de réussite actuel de la fonction de prévision est d'environ 56,8%. Comme @immibis l’a remarqué dans les commentaires, si les suppositions correctes et correctes sont mélangées, le résultat n’est même pas à peine lisible.
Par exemple, cet extrait de code vers la fin du livre:
devient:
En remplaçant les mauvaises suppositions par des tirets bas, nous avons une meilleure idée de ce que la fonction a obtenu:
NB : L'exemple ci-dessus a été créé avec une version précédente du code, travaillant sur la première version du fichier d'entrée.
Code de test
Changer le journal
la source
sidg tlanses,oeth to, shuld hottut tild aoersors Ch, th! Sa, yr! Sheu arinning whales aut ihe e sl he traaty of rrsf tg homn Bho dla tiasot a shab sor ty, af etoors tnd hocket sh bts ait mtubb tiddin tis aeewnrs, dnhost maundy cnd sner aiwt d boelh cheugh -aaieiyns aasiyns taaeiins! th, tla
. Il parvient parfois à obtenir quelques mots complets. Commewhales
.Perl, 2 · 70525 + 326508 = 467558
Prédicteur
Pour exécuter ce programme, vous avez besoin de ce fichier ici , qui doit être nommé
B
. (Vous pouvez modifier ce nom de fichier dans la deuxième instance du caractèreB
ci-dessus.) Voir ci-dessous pour savoir comment générer ce fichier.Le programme utilise une combinaison de modèles de Markov essentiellement comme dans cette réponse de l'utilisateur 2699 , mais avec quelques modifications mineures. Cela produit une distribution pour le caractère suivant. Nous utilisons la théorie de l'information pour décider d'accepter une erreur ou de dépenser une partie de la mémoire en
B
indices de codage (et si oui, comment). Nous utilisons le codage arithmétique pour stocker de manière optimale les bits fractionnaires du modèle.Le programme a une longueur de 582 octets (y compris une nouvelle ligne inutile) et le fichier binaire, une
B
longueur de 69942 octets. Ainsi, selon les règles de notation de plusieurs fichiers , nous obtenons le scoreL
suivant: 582 + 69942 + 1 = 70525.Le programme nécessite presque certainement une architecture 64 bits (little-endian?). Il faut environ 2,5 minutes pour s'exécuter sur une
m5.large
instance sur Amazon EC2.Code de test
Le harnais de test suppose que la soumission est dans le fichier
submission.pl
, mais cela peut facilement être modifié dans la deuxième ligne.Comparaison de texte
Cet échantillon (choisi dans une autre réponse ) apparaît assez tard dans le texte, le modèle est donc assez développé à ce stade. Rappelez-vous que le modèle est complété par 70 kilo-octets "d'indices" qui l'aident directement à deviner les caractères; il ne s'agit pas simplement du court extrait de code ci-dessus.
Générer des astuces
Le programme suivant accepte le code de soumission exact ci-dessus (sur l'entrée standard) et génère le
B
fichier exact ci-dessus (sur la sortie standard):Il faut environ autant de temps que la soumission pour s'exécuter, car il effectue des calculs similaires.
Explication
Dans cette section, nous tenterons de décrire ce que fait cette solution avec suffisamment de détails pour que vous puissiez "l'essayer à la maison" vous-même. La technique principale qui différencie cette réponse des autres consiste à utiliser quelques étapes comme mécanisme de "rembobinage", mais avant d’y parvenir, nous devons définir les bases.
Modèle
L'ingrédient de base de la solution est un modèle de langage. Pour nos besoins, un modèle est quelque chose qui prend une certaine quantité de texte anglais et renvoie une distribution de probabilité sur le caractère suivant. Lorsque nous utiliserons le modèle, le texte anglais sera un préfixe (correct) de Moby Dick. Veuillez noter que la sortie souhaitée est une distribution et non une simple estimation du caractère le plus probable.
Dans notre cas, nous utilisons essentiellement le modèle dans cette réponse par utilisateur2699 . Nous n'avons pas utilisé le modèle de la réponse la plus élevée (autre que la nôtre) d'Anders Kaseorg, précisément parce que nous ne pouvions pas extraire une distribution plutôt qu'une seule hypothèse. En théorie, cette réponse calcule une moyenne géométrique pondérée, mais nous avons obtenu des résultats quelque peu médiocres en interprétant cela trop littéralement. Nous avons "volé" un modèle sur une autre réponse parce que notre "sauce secrète" n'est pas le modèle mais plutôt l'approche globale. Si quelqu'un a un "meilleur" modèle, il devrait être en mesure d'obtenir de meilleurs résultats en utilisant le reste de nos techniques.
Comme remarque, la plupart des méthodes de compression telles que Lempel-Ziv peuvent être considérées comme un "modèle de langage" de cette manière, bien que l'on puisse avoir à plisser les yeux un peu. (C'est particulièrement délicat pour quelque chose qui transforme Burrows-Wheeler!) Notez également que le modèle créé par l'utilisateur 2699 est une modification du modèle de Markov; Essentiellement, rien d’autre n’est concurrentiel pour ce défi ou même pour la modélisation de texte en général.
Architecture globale
Pour faciliter la compréhension, il est agréable de décomposer l’architecture globale en plusieurs parties. Du plus haut niveau, il faut un peu de code de gestion d’état. Ce n’est pas particulièrement intéressant, mais pour être complet, nous tenons à souligner qu’à chaque étape du programme, on demande au programme de supposer, qu’il dispose du préfixe correct de Moby Dick. Nous n'utilisons en aucun cas nos suppositions incorrectes du passé. Pour des raisons d'efficacité, le modèle de langage peut probablement réutiliser son état parmi les N premiers caractères pour calculer son état pour les premiers caractères (N + 1), mais il peut en principe recalculer les éléments à partir de zéro chaque fois qu'il est appelé.
Mettons de côté ce "pilote" de base du programme et jetons un coup d'œil à l'intérieur de la partie qui suppose le prochain caractère. Conceptuellement, il est utile de séparer trois parties: le modèle de langage (décrit ci-dessus), un fichier "astuces" et un "interprète". À chaque étape, l'interprète demandera au modèle de langage une distribution du prochain caractère et lira éventuellement certaines informations du fichier d'indications. Ensuite, il va combiner ces parties dans une supposition. Les informations contenues dans le fichier de conseils ainsi que la manière dont elles seront utilisées seront expliquées ultérieurement, mais pour le moment, il est utile de garder ces parties séparées mentalement. Notez que, du point de vue de la mise en œuvre, le fichier d'indications est littéralement un fichier séparé (binaire), mais il pourrait s'agir d'une chaîne ou de quelque chose stocké dans le programme. À titre approximatif,
Si vous utilisez une méthode de compression standard telle que bzip2 comme dans cette réponse , le fichier "hints" correspond au fichier compressé. L '"interprète" correspond au décompresseur, alors que le "modèle de langage" est un peu implicite (comme mentionné ci-dessus).
Pourquoi utiliser un fichier indice?
Prenons un exemple simple pour approfondir l'analyse. Supposons que le texte comporte des
N
caractères longs et bien approximés par un modèle dans lequel chaque caractère est (indépendamment) la lettreE
avec une probabilité légèrement inférieure à la moitié, deT
même avec une probabilité légèrement inférieure à la moitié etA
avec une probabilité de 1/1000 = 0,1%. Supposons qu'aucun autre caractère ne soit possible; en tout cas, le casA
est assez similaire au cas d'un personnage auparavant invisible, sorti de nulle part.Si nous avons fonctionné dans le régime L 0 (comme le font la plupart des réponses à cette question, mais pas toutes), il n'y a pas de meilleure stratégie pour l'interprète que de choisir l'une des options suivantes:
E
etT
. En moyenne, environ la moitié des caractères seront corrects. Donc E ≈ N / 2 et le score ≈ N / 2 également. Cependant, si nous utilisons une stratégie de compression, nous pouvons alors compresser un peu plus d'un bit par caractère. Puisque L est compté en octets, nous obtenons L ≈ N / 8 et obtenons ainsi N / 4, deux fois plus performant que la stratégie précédente.Obtenir ce taux d'un peu plus d'un bit par caractère pour ce modèle est légèrement non trivial, mais une méthode est le codage arithmétique.
Codage arithmétique
Comme on le sait communément, un codage est une manière de représenter certaines données en bits / octets. Par exemple, ASCII est un codage de 7 bits / caractères de texte anglais et de caractères associés, ainsi que le codage du fichier Moby Dick d'origine à l'étude. Si certaines lettres sont plus courantes que d'autres, un codage à largeur fixe comme ASCII n'est pas optimal. Dans une telle situation, beaucoup de gens se tournent vers le codage de Huffman . Ceci est optimal si vous voulez un code fixe (sans préfixe) avec un nombre entier de bits par caractère.
Cependant, le codage arithmétique est encore meilleur. Grosso modo, il est capable d’utiliser des bits "fractionnaires" pour coder des informations. Il existe de nombreux guides sur le codage arithmétique disponibles en ligne. Nous allons ignorer les détails ici (en particulier de la mise en œuvre pratique, ce qui peut être un peu délicat du point de vue de la programmation) en raison des autres ressources disponibles en ligne, mais si quelqu'un se plaint, cette section sera peut-être plus complète.
Si le texte est réellement généré par un modèle de langage connu, le codage arithmétique fournit un codage essentiellement optimal du texte de ce modèle. Dans un certain sens, cela "résout" le problème de compression pour ce modèle. (Ainsi, dans la pratique, le principal problème est que le modèle n'est pas connu et que certains modèles sont meilleurs que d'autres pour la modélisation de texte humain.) S'il n'était pas permis de commettre des erreurs dans ce concours, dans le langage de la section précédente. Pour résoudre ce problème, une solution aurait consisté à utiliser un encodeur arithmétique pour générer un fichier "astuces" à partir du modèle de langage, puis à utiliser un décodeur arithmétique comme "interprète".
Dans ce codage essentiellement optimal, nous finissons par dépenser -log_2 (p) bits pour un caractère de probabilité p, et le débit global du codage correspond à l' entropie de Shannon . Cela signifie qu’un caractère dont la probabilité est proche de 1/2 nécessite environ un bit à encoder, tandis que celui ayant une probabilité de 1/1000 utilise environ 10 bits (car 2 ^ 10 équivaut à environ 1000).
Mais la métrique de score pour ce défi a été bien choisie pour éviter la compression comme stratégie optimale. Nous devrons trouver un moyen de faire des erreurs comme compromis pour obtenir un fichier d'indices plus court. Par exemple, une stratégie que l’on pourrait essayer est une stratégie de branchement simple: nous essayons généralement d’utiliser un encodage arithmétique lorsque nous le pouvons, mais si la distribution de probabilité du modèle est "mauvaise" d’une certaine manière, nous devinons simplement le caractère le plus probable et ne le faisons pas. Ne pas essayer de l’encoder.
Pourquoi faire des erreurs?
Analysons l'exemple d'avant afin de motiver pourquoi nous pourrions vouloir faire des erreurs "intentionnellement". Si nous utilisons le codage arithmétique pour coder le caractère correct, nous dépenserons environ un bit dans le cas d’un
E
ouT
, mais environ dix bits dans le cas d’unA
.Globalement, il s’agit d’un très bon encodage, dépensant un peu plus par caractère même s’il existe trois possibilités; fondamentalement, cela
A
est assez improbable et nous ne finissons pas par dépenser trop souvent les dix bits correspondants. Cependant, ne serait-il pas agréable de pouvoir commettre une erreur dans le cas d'unA
? Après tout, la métrique du problème considère que 1 octet = 8 bits de longueur équivaut à 2 erreurs; il semble donc qu'il faille préférer une erreur au lieu de dépenser plus de 8/2 = 4 bits sur un caractère. Dépenser plus d'un octet pour enregistrer une erreur semble définitivement sous-optimal!Le mécanisme de "rembobinage"
Cette section décrit le principal aspect astucieux de cette solution, à savoir un moyen de gérer gratuitement les suppositions incorrectes.
Pour l'exemple simple que nous avons analysé, le mécanisme de rembobinage est particulièrement simple. L'interprète lit un bit du fichier d'indications. Si c'est un 0, il devine
E
. Si c'est un 1, il devineT
. La prochaine fois qu'il est appelé, il voit quel est le bon caractère. Si le fichier d’indications est bien configuré, nous pouvons nous assurer que dans le cas d’unE
ouT
, l’interprète devine correctement. Mais qu'en est-ilA
? L'idée du mécanisme de rembobinage est de ne pas coderA
du tout . Plus précisément, si l'interprète apprend plus tard que le caractère correct est unA
, il métaphoriquement " rembobine la bande": il renvoie le bit lu précédemment. Le bit lu a bien l'intention de coderE
ouT
, mais pas maintenant; il sera utilisé plus tard. Dans cet exemple simple, cela signifie qu’il continue à deviner le même caractère (E
ouT
) jusqu’à ce qu’il comprenne bien; alors il lit un autre bit et continue.Le codage de ce fichier de conseils est très simple: convertissez tous les
E
s en 0 bits etT
s en 1 bits, tout en ignorantA
s complètement. D'après l'analyse à la fin de la section précédente, ce schéma fait quelques erreurs mais réduit le score dans son ensemble en ne codant aucun desA
s. En tant qu'effet plus petit, il enregistre en fait la longueur du fichier d'indications également, car nous finissons par utiliser exactement un bit pour chacunE
etT
au lieu de légèrement plus qu'un peu.Un petit théorème
Comment décidons-nous quand faire une erreur? Supposons que notre modèle nous donne une distribution de probabilité P pour le caractère suivant. Nous allons séparer les caractères possibles en deux classes: codée et non codée . Si le caractère correct n'est pas codé, nous utiliserons ensuite le mécanisme de "rembobinage" pour accepter une erreur sans frais en longueur. Si le bon caractère est codé, nous utiliserons une autre distribution Q pour le coder en utilisant un codage arithmétique.
Mais quelle distribution Q devrions-nous choisir? Il n'est pas trop difficile de voir que les caractères codés devraient tous avoir une probabilité plus élevée (en P) que les caractères non codés. En outre, la distribution Q ne devrait inclure que les caractères codés; après tout, nous ne codons pas les autres, nous ne devrions donc pas "dépenser" de l'entropie sur eux. Il est un peu plus délicat de voir que la distribution de probabilité Q devrait être proportionnelle à P sur les caractères codés. Rassembler ces observations signifie que nous devrions coder les caractères les plus probables, mais peut-être pas les caractères les moins probables, et que Q est simplement redimensionné sur les caractères codés.
De plus, il s'avère qu'il existe un théorème intéressant concernant la "coupure" à choisir pour les caractères de codage: vous devez coder un caractère tant qu'il est au moins égal à 1 / 5.393 aussi vraisemblable que les autres caractères codés combinés. Ceci "explique" l'apparition de la constante apparemment aléatoire vers
5.393
la fin du programme ci-dessus. Le nombre 1 / 5.393 ≈ 0.18542 est la solution à l'équation -p log (16) - p log p + (1 + p) log (1 + p) = 0 .C'est peut-être une idée raisonnable d'écrire cette procédure en code. Cet extrait est en C ++:
Mettre tous ensemble
La section précédente est malheureusement un peu technique, mais si nous assemblons toutes les autres pièces, la structure est la suivante. Chaque fois que le programme est invité à prédire le caractère suivant après un caractère correct donné:
L'encodage du fichier d'indications fonctionne de la même manière. Dans ce cas, le programme sait quel est le bon caractère suivant. Si c'est un caractère qui devrait être codé, alors bien sûr, vous devriez utiliser le codeur arithmétique dessus; mais s'il s'agit d'un caractère non codé, il ne met simplement pas à jour l'état du codeur arithmétique.
Si vous comprenez le contexte théorique de l'information tel que les distributions de probabilité, l'entropie, la compression et le codage arithmétique mais que vous avez essayé de comprendre cet article (sauf pourquoi le théorème est vrai), laissez-nous savoir et nous pouvons essayer de clarifier les choses. Merci d'avoir lu!
la source
B
fichier? Si oui, pouvez-vous inclure cela dans votre réponse?Python 3, 2 · 267 + 510193 = 510727
Prédicteur
Il utilise une combinaison bayésienne pondérée de l’ordre 0,…, 16 modèles de Markov, avec des poids [1, 6, 12, 30, 65, 99, 87, 117, 118, 89, 95, 118, 96, 184, 126, 219, 126].
Le résultat n'est pas très sensible au choix de ces poids, mais je les ai optimisés car je pouvais utiliser le même algorithme d' acceptation tardive que celui que j'ai utilisé dans ma réponse à «Assembler une majorité au Sénat» , où chaque mutation candidate est juste un incrément de ± 1 pour un seul poids.
Code de test
la source
b"\0\3\6\r\34'&-20'\22!P\n[\26"
est la représentation ascii des poids, où les petites valeurs non imprimables s’échappent en octal.Python 3 ,
2 * 279 + 592920 = 5934782 * 250 + 592467 = 5929672 * 271 + 592084 = 5926262 * 278 + 592059 = 5926152 * 285 + 586660 = 5872302 * 320 + 585161 = 5858012 * 339 + 585050 = 585728Essayez-le en ligne!
Une fonction utilisant des variables globales. Apprend au fur et à mesure, en construisant un modèle au niveau du mot: étant donné ce que l’on voit jusqu’à présent dans ce mot , quel est le prochain caractère le plus courant? Au fur et à mesure que plus d’entrées entrent, il apprend assez bien les mots courants du texte et le caractère le plus courant pour commencer le mot suivant .
Par exemple:
Cela ne va pas très bien au début, mais à la fin, il y a beaucoup de mots qui sortent. L'option de secours est un espace et après un espace unique, un "a", à moins que la lettre précédente ne soit "nedtfo", un chiffre, un trait d'union ou une apostrophe. Il prédit également de manière agressive les coupures de ligne après 71 caractères, ou si un espace est attendu après 66. Ces deux paramètres ont été ajustés aux données ("t" est beaucoup plus courant après un espace, mais a déjà été prédit plus souvent, donc " un "est une meilleure hypothèse en dehors de ces six cas spéciaux).
Apprendre quelles paires de mots allaient ensemble et préconfigurer la cartographie s’est avéré inutile.
Il se termine avec un texte comme celui-ci:
ce qui correspond à cette partie de l'entrée:
Vous pouvez voir où les noms propres en particulier ressortent plutôt bien, mais la fin des mots est également généralement juste. Quand on voit "dou", on s'attend à un "doute", mais une fois que le "l" apparaît, il devient "doubloon".
Si vous l'exécutez une seconde fois avec le même modèle, il vient de générer immédiatement une autre conversion correcte de 92k (51,7% -> 59,3%), mais elle reste toujours à un peu moins de 60% à partir de la deuxième itération.
Le code de mesure est dans le lien TIO, ou voici une version légèrement meilleure:
guess.txt
a la sortie devinée à la fin.la source
C ++, note: 2 * 132 + 865821 = 866085
Merci à @Quentin d'avoir sauvé 217 octets!
Une solution très simple qui, à partir d’un caractère, n’affiche que le caractère qui apparaît le plus souvent après le caractère saisi.
Vérifiez le score avec:
Edit: Utiliser
whale2.txt
donne un meilleur score.la source
L
pour sauvegarder un tas de caractères :)Python, 2 * 516 + 521122 = 522154
Algorithme:
Encore une autre soumission en python, cet algorithme calcule la prochaine lettre la plus probable qui regarde les séquences de longueur 1, ..., l. La somme des probabilités est utilisée et il existe quelques astuces pour obtenir de meilleurs résultats.
Résultats:
Généralement du charabia, même si vous pouvez le voir reprendre une phrase occasionnelle, telle que "Père Mapple".
Code de test:
Assez simple, sort quelques exemples du texte à différents points. Utilise whale2.txt, car cela évite une logique supplémentaire pour calculer les nouvelles lignes.
la source
C (gcc) ,
6797876528928476 octets,679619652740 suppositions incorrectesEssayez-le en ligne!
Mise à jour: ~ 27 000 points avec le fichier mis à jour, 16 points (8 octets) avec une fonction de meilleure qualité.
Explication
La façon dont cela fonctionne est que, lorsque le code parcourt le texte, il mémorise le dernier caractère qui a mis fin à une séquence de 4 caractères donnée et renvoie cette valeur. Un peu similaire à l'approche d'Arnauld ci-dessus, mais repose sur la probabilité inhérente que deux séquences de 4 caractères données se terminent de la même manière.
De-golfé:
la source
sh + bzip2, 2 * 364106 = 728212
2 * 381249 + 0 = 762498suivi du whale2.txt compressé avec bzip2 avec le premier octet manquant
Ignore son entrée; affiche la bonne réponse. Cela fournit une ligne de base à une extrémité; daniero fournit une ligne de base à l'autre extrémité.
Script de constructeur:
Faisceau de test I / O (tcc; couper la première ligne pour gcc). Ce faisceau de test peut être utilisé par n’importe qui sur une plate-forme appropriée qui soumet un programme complet qui attend des E / S en lecture / écriture. Il utilise des E / S octet à la fois pour éviter les tricheries. Le programme enfant doit vider la sortie après chaque octet pour éviter le blocage.
la source
but may not load any other external files, and your code may not access the whale.txt file in any way other than described above.
clause?nth
Temps sera donné le nième caractèrewhale.txt
ouwhale2.txt
et il doit sa sortie estimation pour la(n+1)th
personnage." - Comment cette exigence est-elle remplie? Le code affiche le texte entier dewhale.txt
chaque fois qu'il est exécuté.Python 3 , 879766
Essayez-le en ligne!
... La
///
réponse qui imprime un espace obtient 10 votes positifs, alors que mon code ne peut en obtenir que 3 ...Explication:
Pour chaque personnage, le programme:
frequency[prev][char]
frequency[char]
qui ont le score total
Comme il n’existe aucun moyen de télécharger un fichier volumineux vers TIO (sauf demander à Dennis), l’exemple exécuté dans le lien TIO n’exécute le programme que pour une petite partie du texte.
En comparaison avec la réponse précédente, celle-ci comporte 362 caractères incorrects de plus, mais le code est plus court de 255 octets. Le multiplicateur fait que ma soumission a un score inférieur.
la source
C #, 378 * 2 + 569279 = 570035
Cette approche utilise une table de correspondance pour apprendre le caractère le plus courant qui suit une chaîne donnée. Les clés de la table de correspondance ont un maximum de 4 caractères. La fonction met donc d'abord à jour la table de correspondance avec le caractère actuel, puis vérifie simplement quel caractère est le plus susceptible de se produire après les 4 précédents, y compris celui en cours. . Si ces 4 caractères ne sont pas trouvés dans la table de correspondance, une espace est imprimée.
Cette version utilise le
whale2.txt
fichier, car elle améliore considérablement le nombre de suppositions réussies.Voici le code utilisé pour tester la classe:
Le code s'exécute en à peine 2 secondes. Pour mémoire, voici ce que je reçois lorsque je modifie la taille des clés de la table de correspondance (inclut les résultats d'une seconde exécution sans réinitialiser le modèle):
Il serait intéressant de savoir pourquoi une taille de clé de 4 caractères est le meilleur choix dans cet algorithme.
Comparaison de texte
Original:
Recréé:
Suppositions:
Changer le journal
whale2.txt
par l'optimisation ainsi supprimée.la source
Java 7, 1995 caractères, (1995 * 2 + 525158) 529148
Java est nul pour les petites tailles de programme. Quoi qu'il en soit, j'ai essayé plusieurs approches extrêmement complexes et délicates qui ont produit des résultats étonnamment nuls. Par la suite, j’y suis retourné et n’ai fait qu’une approche simple, ce qui a permis de réduire la taille du programme et d’améliorer les résultats.
Cette approche est en réalité extrêmement simple. Il introduit aveuglément les x caractères précédents (en plus de toutes les sous-chaînes de ces caractères) dans une table de hachage mappée sur le caractère actuel. Il garde ensuite trace des modèles qui prédisent le plus précisément le caractère actuel. Si les motifs qui précèdent certains caractères sont rencontrés plusieurs fois, ils parviennent à prédire le caractère. Il donne la priorité aux chaînes plus longues et au caractère qui suit le plus souvent une chaîne donnée. Cet algorithme ne sait rien du type de document ou de la langue anglaise.
J'ai choisi d'utiliser 9 caractères et d'essayer de faire correspondre les mots entiers de ces 9 caractères précédents lorsque cela était possible. Lorsque vous n'essayez pas de faire correspondre les mots dans les chaînes, la longueur optimale est de 6 caractères, ce qui génère plusieurs milliers de mauvaises prédictions.
Une observation intéressante était que l'utilisation de 20 caractères entraînait de mauvaises prédictions la première fois mais une précision de 99,9% lors des passages suivants. L'algorithme était fondamentalement capable de mémoriser le livre par superposition de morceaux de 20 octets, ce qui était suffisamment distinct pour lui permettre de rappeler le livre entier, un caractère à la fois.
la source
Python 3,
2 × 497 + 619608 = 6206022 × 496 + 619608 = 620600J'ai essayé cela de façon indépendante, mais je me suis retrouvé avec une version inférieure de la réponse de Michael Homer. J'espère que cela ne rend pas ma réponse complètement obsolète.
Ceci construit au fil du temps un dictionnaire de mots (défini grossièrement comme des chaînes terminées par
ou
\n
, sensibles à la casse et incluant la ponctuation). Il recherche ensuite dans ce dictionnaire les mots commençant par ce qu'il sait jusqu'ici du mot actuel, trie la liste résultante par fréquence d'apparition (lentement) et suppose que le caractère suivant est le caractère suivant du mot correspondant le plus commun. Si nous avons déjà le mot correspondant le plus commun ou s'il n'existe plus, le mot correspondant est renvoyé.
Il crée également un dictionnaire de paires de mots dégoûtant et inefficace. En atteignant une limite de mot, il devine que le caractère suivant est la première lettre du deuxième mot de la paire de mots correspondante la plus courante, ou
t
s'il n'y a pas de correspondance. Ce n'est pas très intelligent, cependant. EnsuiteMoby
, le programme devine correctement que le caractère suivant estD
, mais il oublie tout du contexte et finit généralement par appeler la baleine "Moby Duck" (car le mot "néerlandais" semble être plus fréquent dans la première moitié du texte. ). Il serait facile de résoudre ce problème en donnant la priorité aux paires de mots aux mots individuels, mais je m'attends à ce que le gain soit marginal (puisqu'il est généralement correct à partir du troisième caractère et que les paires de mots ne sont pas très utiles au départ).Je pourrais ajuster ceci pour mieux correspondre au texte fourni, mais je ne pense pas que le réglage manuel de l'algorithme basé sur une connaissance préalable de l'entrée est vraiment dans l'esprit du jeu, donc, à part choisir t comme caractère de secours après un espace ( et je n'aurais probablement pas dû le faire non plus), je l'ai évité. J'ai ignoré la longueur de ligne connue du fichier d'entrée et inséré à la place
\n
toutes les 13 espaces; il s'agit certainement d'une correspondance très médiocre. L'intention principale était de garder la longueur de ligne raisonnable plutôt que de faire correspondre l'entrée.Le code n'est pas très rapide (environ 2 heures sur ma machine), mais dans l'ensemble, environ la moitié des caractères sont corrects (49%). Je m'attends à ce que le score soit légèrement meilleur si on continue
whale2.txt
, mais je ne l'ai pas fait.Le début de la sortie ressemble à ceci:
mais à la fin, ça ressemble un peu plus à ... quelque chose. Mon passage préféré de la fin du livre,
sort comme
Cela aurait rendu The Wrath of Khan beaucoup plus déroutant. Et "solitaire" → "tingly" est une substitution particulièrement satisfaisante.
Éditer: un octet enregistré en supprimant un espace superflue
Notation
Ceci exécute le programme pour le texte de Moby Dick et génère le texte "prédit" textto stdout, et abuse de stderr pour écrire le score. Je vous recommande de rediriger la sortie vers un fichier.
la source
lambda i:i[1]
moins cher que de traiter avecoperator
?C ++, 2 · 62829 + 318786 = 444444
Pour exécuter ce programme, vous avez besoin de ce fichier ici , qui doit être nommé
C
.Le programme utilise la même combinaison de modèles de Markov que dans notre réponse précédente . Comme précédemment, cette combinaison est essentiellement le modèle de cette réponse de l'utilisateur 2699 , mais avec quelques petites modifications.
Voyant comment cette réponse utilise exactement le même modèle qu'auparavant, l'amélioration est un meilleur mécanisme de théorie de l' information que le mécanisme de "rembobinage" décrit précédemment. Cela lui permet de faire moins d’erreurs tout en ayant une longueur combinée plus petite. Le programme lui-même ne joue pas beaucoup au golf, car il n’est pas le principal contributeur au score.
Le programme est 2167 octets (y compris tous les onglets pour l' indentation et beaucoup d'autres caractères inutiles, mais avant le code de test) et le fichier binaire
C
est 60661 octets, donc dans les règles de notation fichiers multiples , nous marquonsL
en 2167 + 60661 + 1 = 62829.Le programme prend environ 8 minutes pour s'exécuter sur une
m5.4xlarge
instance sur Amazon EC2 et utilise un peu plus de 16 Go de mémoire. (Cette utilisation excessive de la mémoire n'est pas nécessaire - nous ne l'avons pas optimisée non plus.)la source
Python 3, 526640
274 octets, 526092 erreurs (utilisation
whale2.txt
). Ceci est certainement capable d'amélioration, mais il a atteint le stade "assez bon pour poster".L'idée est de stocker les fréquences de toutes les exécutions de 2, 3, 4, ..., 10 caractères. Pour chacune de ces longueurs L, nous vérifions si les derniers caractères L-1 correspondent à un modèle enregistré; si tel est le cas, notre hypothèse g L est le caractère suivant le plus fréquent suivant ce modèle. Nous recueillons jusqu'à neuf suppositions de cette façon. Pour décider quelle hypothèse utiliser, nous pondérons la fréquence de chaque motif par sa longueur jusqu'à la 8ème puissance. La supposition avec la plus grande somme de fréquences pondérées est choisie. Si aucun motif ne correspond, nous devinons l'espace.
(La longueur maximale du motif et l'exposant de pondération ont été choisis par essais pour donner le moins de suppositions incorrectes.)
Voici ma version de travail en cours non golfée:
Et le harnais de test:
Voici un exemple de sortie du début du texte. Déjà nous commençons à voir la possibilité de terminer mots après avoir vu leur première lettre (
in
,to
,and
,by
, aussi, apparemment,school
).Vers la fin, il y a encore beaucoup d'erreurs, mais aussi de très bonnes séquences (
shmage seashawks
par exemple).Il est intéressant de regarder certaines des erreurs et de deviner le mot "attendu" de l'algorithme. Par exemple, après
sail
, le programme prédit les deux foiso
- poursailor
, je présume. Ou encore, après, a
avoirn
attendu - peut-être à cause de la fréquence courante de, and
.Changelog:
la source
Python 2, note: 2 * (407 + 56574) + 562262 = 676224
Recherches pour les mots qui correspondent aux caractères précédents dans une liste de
tousles mots les plus utilisés dans le texte, triés par le nombre de leurs occurrences.Code:
Données: https://www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0
Suite de tests:
Edit: Utiliser
whale2.txt
donne un meilleur score.la source
C ++ (GCC), 725 × 2 + 527076 = 528526
Encore une autre soumission préfixe-fréquence. Courez dessus
whale2.txt
et obtenez un score similaire (légèrement pire) que les autres.Celui-ci trouve avidement la plus longue chaîne qui commence par un suffixe de l'historique, et s'il y a plusieurs candidats, tie-break avec des chaînes plus courtes.
Par exemple: Si les 7 derniers caractères sont
abcdefgh
, et la chaîneabcdefghi
etabcdefghj
apparaît avec la plus grande fréquence dans toutes les chaînes de la formeabcdefgh*
, la sortie sera soiti
ouj
, tie - break avec des suffixes plus courtes (bcdefgh
,cdefgh
, ...).Pour des raisons inconnues, rien de plus de 7 et mon ordinateur n'a pas assez de RAM pour l'exécuter. Même avec 7, je dois fermer tous les navigateurs Web pour l'exécuter.
Code de test:
Ungolfed:
Exemple de sortie:
Celui-ci est proche de la fin du texte. La plupart des mots longs sont prévus tout à fait avec précision (
intervals
,pivot-hole
,distance
)Les majuscules ne semblent pas bonnes.
la source
Python 2, 756837
Utilise quelque chose qui pourrait être des chaînes de Markov?
la source
zlib.decompress('...')
évalue{'G?':' ', 'G;':' ','G"':' ',.......}
eta
correspond à un dictionnaire qui mappe de 2 à 1 caractère. Variante à deux caractères de la réponse de Steadybox .xxd
,hexdump
,uuencode
Ou similaireHaskell, (1904 + 1621 + 208548 + 25646) * 2 + 371705 = 847143
Exemple:
Utilise trois fichiers auxiliaires pré-calculés:
seqwords
contient les 236 mots les plus courants.wordsseq
contient une séquence LZMA-compressée de ces mots, et pour tous les mots qui ne font pas partie des 236 plus courants, la longueur.dicttries
contient, pour chaque longueur de mot, un arbre de décision contenant tous les mots restants. De ces essais, les entrées sont sélectionnées au fur et à mesure.De cette manière, nous obtenons un taux d'erreur nettement inférieur à celui de tous les autres systèmes avec pertes; Malheureusement, le
wordsseq
fichier est encore trop gros pour être compétitif.Voici une version complète qui crée les fichiers et effectue l'analyse:
la source
C ++ (WIP), 1923 * 2 + 1017344 = 1021190
Dans l'état actuel des choses, cette solution est WIP et donc non golfée. Considérant également que la taille réelle du code n’a pratiquement aucun impact sur le score, j’ai pensé poster ma réponse avant de commencer à l’optimiser.
(Code complet disponible ici: https://github.com/BrainStone/MobyDickRNG - Inclut le programme complet et la recherche de semences)
Cette solution est basée sur un RNG. J'analyse d'abord le texte. Je crée une carte qui compte les occurrences de deux caractères consécutifs. Ensuite, je crée une carte de distribution. Tout cela est fait de manière statique, donc devrait être en conformité avec les règles.
Ensuite, en essayant d’imprimer le texte, je fais une recherche et tire un caractère aléatoire de ceux qui sont possibles. Bien que cela produise généralement des résultats pires que la simple publication de la lettre suivante la plus courante, il existe probablement des semences divines qui produiront de meilleurs résultats. C'est pourquoi la graine est codée en dur. Je suis actuellement à la recherche de la meilleure graine. Et je mettrai à jour cette réponse une fois que je trouverai de meilleures semences. Alors restez au courant!
Si quelqu'un veut rechercher lui-même des semences ou utiliser des GNR différents, n'hésitez pas à débourser le montant de la pension.
Méthode utilisée pour calculer le score: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15
Notez que même si le score total est le pire pour le moment, il bat le nombre d'erreurs de la simple sortie d'espaces. Et les chances sont bonnes que le score diminue en vérifiant plus de graines.
Changelog
Graines contrôlées: 0-50000. Note: 2305 * 2 + 1017754 = 1022364
Graines contrôlées: 0-80000. Note: 1920 * 2 + 1017754 = 1021594 (-770)
Graines vérifiées: 0-32000000. Note: 1923 * 2 + 1017344 = 1021190 (-404)
la source
Ruby, 1164418 (ouch)
Je voulais juste voir comment je pouvais faire sans vérifier toutes les autres réponses.
Je ne sais pas si cela est autorisé, car il inclut un littéral généré par l'analyse du fichier, mais même si ce n'était pas le cas, ce n'était pas comme s'il risquait de frapper quelqu'un.
Comment j'ai généré
x
Tout d'abord, j'ai généré
a.txt
avec ce qui suit:Puis j'ai généré
a.csv
:Ensuite, je l'ai analysé
x
avec le script Ruby suivant:Comment j'ai marqué
la source
Python 3 , (146 * 2 + 879757) 880049 octets
Essayez-le en ligne!
Table de fréquence assez simple. Chaque position dans la chaîne correspond au code ascii du caractère actuel (moins 10 = 0x0a = '\ n', le caractère le plus bas du fichier) et le caractère à chaque index est le prochain caractère le plus fréquent. En supposant que je calcule les fréquences correctement…
Testé avec le code du test de user202729
la source
def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]
?[Python 3] (644449 * 2 + 0) 1288898 points
Une précision parfaite dans seulement 644449 octets
Le code complet ne peut tenir dans une réponse, aussi je l'ai mis ici et j'ai remplacé le gros littéral de chaîne binaire par b '###' dans le texte de la réponse.
Ceci est généré avec le code suivant, où "modified.py" est le fichier généré et "cheatsheet.txt" est le fichier whale2.txt qui commence au deuxième caractère.
Le code peut être exécuté en ajoutant ce qui suit à la fin de "modified.py". "whale2.txt" doit être dans le même répertoire que "modified.py", et le résultat sera écrit dans "out.txt".
Cette réponse n'accède pas directement à whale.txt ou à whale2.txt. Il utilise les bibliothèques de compression standard existantes comme cela est explicitement autorisé dans les règles.
la source