Écrivez Moby Dick, environ


297

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 . Votre score sera

2*L + E

Lest la taille de votre soumission en octets et Ele 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.txtou whale2.txtet devra deviner pour le ( n + 1 ) ème caractère. La Ecomposante 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 staticdes 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 Epartie 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 Lcomposante 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 Lscore.

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.txtouwhale2.txtdé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.txtle 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.txtest 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.


Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée pour discuter .
Dennis

9
xkcd.com/1960 semble être une référence à ce défi!
A. Rex

J'ai pensé compresser cela ... mais c'est un peu trop long que mon ordinateur se soit
cassé les

Réponses:


135

/// , 2 * 1 + 1020874 = 1020876

 

Imprime un espace.


Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée pour discuter .
Dennis

C'est un piratage de récompense extrêmement intelligent! Vous devez être un AGI;)
Alex

97

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.

a=[...l='14210100'],m={},s={},b={}
f=c=>a.some((t,n)=>x=s[y=l.slice(n)]>t|/^[A-Z '"(]/.test(y)&&b[y],l+=String.fromCharCode(c),a.map((_,n)=>(m[x=l.slice(n)]=-~m[x])<s[y=l.slice(n,8)]||(s[y]=m[x],b[y]=c)),l=l.slice(1))&&x||32

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:

    Here be it said, that this pertinacious pursuit of one particular whale,[LF]
    continued through day into night, and through night into day, is a thing[LF]
    by no means unprecedented in the South sea fishery.
    

    devient:

    "e e be it said, that thes woacangtyous sarsuet of tie oort cular thale[LF][LF]
     orsinued toeough tir on e togh   and sheough toght an o ters af t shin[LF][LF]
    be to means insrocedented tn hhe sputh Sevsaonh ry,
    

    En remplaçant les mauvaises suppositions par des tirets bas, nous avons une meilleure idée de ce que la fonction a obtenu:

    _e_e be it said, that th_s _____n___ous __rsu_t of __e __rt_cular _hale_[LF]
    _o__inued t__ough ___ _n__ __gh__ and _h_ough __ght _n_o ____ __ _ _hin_[LF]
    b_ _o means _n_r_cedented _n _he __uth _e_____h_ry_
    

    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

/**
  The prediction function f() and its variables.
*/
a=[...l='14210100'],m={},s={},b={}
f=c=>a.some((t,n)=>x=s[y=l.slice(n)]>t|/^[A-Z '"(]/.test(y)&&b[y],l+=String.fromCharCode(c),a.map((_,n)=>(m[x=l.slice(n)]=-~m[x])<s[y=l.slice(n,8)]||(s[y]=m[x],b[y]=c)),l=l.slice(1))&&x||32

/**
  A closure containing the test code and computing E.
  It takes f as input.
  (f can't see any of the variables defined in this scope.)
*/
;
(f => {
  const fs = require('fs');

  let data = fs.readFileSync('whale2.txt'),
      len = data.length,
      err = 0;

  console.time('ElapsedTime');

  data.forEach((c, i) => {
    i % 100000 || console.log((i * 100 / len).toFixed(1) + '%');

    if(i < len - 1 && f(c) != data[i + 1]) {
      err++;
    }
  })

  console.log('E = ' + err);
  console.timeEnd('ElapsedTime');
})(f)

Changer le journal

  • 524727 - 19644 points enregistrés en passant à whale2.txt (mise à jour du défi)
  • 544371 - 327 points enregistrés en forçant les modèles commençant par une lettre majuscule, une citation, une double citation ou une parenthèse ouvrante
  • 544698 - 2119 points enregistrés en forçant les modèles commençant par un espace à être toujours fiables
  • 546817 - A enregistré 47 points en ajustant les seuils et en jouant au golf avec la fonction de prédiction
  • 546864 - 1496 points enregistrés en étendant la longueur maximale du motif à 8 caractères
  • 548360 - 6239 points économisés en introduisant la notion de modèles de confiance, avec des seuils dépendant de leur longueur
  • 554599 - gain de 1030 points en améliorant la prévision de saut de ligne
  • 555629 - 22 points enregistrés en jouant au golf avec la fonction de prédiction
  • 555651 - a enregistré 40 points en jouant au golf avec la fonction de prédiction
  • 555691 - score initial

44
Pour les curieux, non, cela ne produit rien de tel que Moby Dick. C'est beaucoup de 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. Comme whales.
Immibis

23
@immibis Le titre du défi a été choisi judicieusement. Ceci est Moby Dick, environ . :-)
Arnauld

3
@Nathaniel Il y a eu beaucoup de mises à jour, donc ce serait à peine lisible et pas vraiment informatif. J'ai ajouté un journal des modifications avec de brèves explications sur les améliorations.
Arnauld

45
Je pense que votre programme est en train de faire une traduction parfaite en gaélique.
Beska

1
@ Draco18s Il est difficile de dire si cette virgule était une bonne ou une mauvaise hypothèse. Si c'était une mauvaise idée, la fonction de prédiction aurait peut-être légitimement tenté de mettre une lettre après n'importe quelle lettre -autre-ce qui était réellement là au lieu de la virgule une fois qu'elle l'a reçue.
Arnauld

91

Perl, 2 · 70525 + 326508 = 467558

Prédicteur

$m=($u=1<<32)-1;open B,B;@e=unpack"C*",join"",<B>;$e=2903392593;sub u{int($_[0]+($_[1]-$_[0])*pop)}sub o{$m&(pop()<<8)+pop}sub g{($h,%m,@b,$s,$E)=@_;if($d eq$h){($l,$u)=(u($l,$u,$L),u($l,$u,$U));$u=o(256,$u-1),$l=o($l),$e=o(shift@e,$e)until($l^($u-1))>>24}$M{"@c"}{$h}++-++$C{"@c"}-pop@c for@p=($h,@c=@p);@p=@p[0..19]if@p>20;@c=@p;for(@p,$L=0){$c="@c";last if" "ne pop@c and@c<2 and$E>99;$m{$_}+=$M{$c}{$_}/$C{$c}for sort keys%{$M{$c}};$E+=$C{$c}}$s>5.393*$m{$_}or($s+=$m{$_},push@b,$_)for sort{$m{$b}<=>$m{$a}}sort keys%m;$e>=u($l,$u,$U=$L+$m{$_}/$s)?$L=$U:return$d=$_ for sort@b}

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ère Bci-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 Bindices 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 Blongueur de 69942 octets. Ainsi, selon les règles de notation de plusieurs fichiers , nous obtenons le score Lsuivant: 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.largeinstance sur Amazon EC2.

Code de test

# Golfed submission
require "submission.pl";

use strict; use warnings; use autodie;

# Scoring length of multiple files adds 1 penalty
my $length = (-s "submission.pl") + (-s "B") + 1;

# Read input
open my $IN, "<", "whale2.txt";
my $input = do { local $/; <$IN> };

# Run test harness
my $errors = 0;
for my $i ( 0 .. length($input)-2 ) {
    my $current = substr $input, $i, 1;
    my $decoded = g( $current );

    my $correct = substr $input, $i+1, 1;
    my $error_here = 0 + ($correct ne $decoded);
    $errors += $error_here;
}

# Output score
my $score = 2 * $length + $errors;
print <<EOF;
length $length
errors $errors
score  $score
EOF

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

"And did none of ye see it before?" cried Ahab, hailing the perched men all around him.\\"I saw him almost that same instant, sir, that Captain 
"And wid note of te fee bt seaore   cried Ahab, aasling the turshed aen inl atound him. \"' daw him wsoost thot some instant, wer, that Saptain 
"And _id no_e of _e _ee _t _e_ore__ cried Ahab, _a_ling the __r_hed _en __l a_ound him._\"_ _aw him ___ost th_t s_me instant, __r, that _aptain 

Ahab did, and I cried out," said Tashtego.\\"Not the same instant; not the same--no, the doubloon is mine, Fate reserved the doubloon for me. I 
Ahab aid  ind I woued tut,  said tashtego, \"No, the same instant, tot the same -tow nhe woubloon ws mane. alte ieserved the seubloon ior te, I 
Ahab _id_ _nd I ___ed _ut,_ said _ashtego__\"No_ the same instant_ _ot the same_-_o_ _he _oubloon _s m_ne_ __te _eserved the __ubloon _or _e_ I 

only; none of ye could have raised the White Whale first. There she blows!--there she blows!--there she blows! There again!--there again!" he cr
gnly  towe of ye sould have tersed the shite Whale aisst  Ihere ihe blows! -there she blows! -there she blows! Ahere arains -mhere again!  ce cr
_nly_ _o_e of ye _ould have ___sed the _hite Whale _i_st_ _here _he blows!_-there she blows!_-there she blows! _here a_ain__-_here again!_ _e cr

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 Bfichier exact ci-dessus (sur la sortie standard):

@S=split"",join"",<>;eval join"",@S[0..15,64..122],'open W,"whale2.txt";($n,@W)=split"",join"",<W>;for$X(0..@W){($h,$n,%m,@b,$s,$E)=($n,$W[$X]);',@S[256..338],'U=0)',@S[343..522],'for(sort@b){$U=($L=$U)+$m{$_}/$s;if($_ eq$n)',@S[160..195],'X<128||print(pack C,$l>>24),',@S[195..217,235..255],'}}'

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 Ncaractères longs et bien approximés par un modèle dans lequel chaque caractère est (indépendamment) la lettre Eavec une probabilité légèrement inférieure à la moitié, de Tmême avec une probabilité légèrement inférieure à la moitié et Aavec une probabilité de 1/1000 = 0,1%. Supposons qu'aucun autre caractère ne soit possible; en tout cas, le cas Aest 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: Eet T. 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 Eou T, mais environ dix bits dans le cas d’un A.

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 Aest 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'un A? 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 devine T. 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’un Eou T, l’interprète devine correctement. Mais qu'en est-il A? L'idée du mécanisme de rembobinage est de ne pas coder Adu tout . Plus précisément, si l'interprète apprend plus tard que le caractère correct est un A, il métaphoriquement " rembobine la bande": il renvoie le bit lu précédemment. Le bit lu a bien l'intention de coder EouT, mais pas maintenant; il sera utilisé plus tard. Dans cet exemple simple, cela signifie qu’il continue à deviner le même caractère ( Eou T) 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 Es en 0 bits et Ts en 1 bits, tout en ignorant As 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 des As. 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 chacun Eet Tau 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.393la 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 ++:

// Assume the model is computed elsewhere.
unordered_map<char, double> model;

// Transform p to q
unordered_map<char, double> code;
priority_queue<pair<double,char>> pq;
for( char c : CHARS )
    pq.push( make_pair(model[c], c) );
double s = 0, p;
while( 1 ) {
    char c = pq.top().second;
    pq.pop();
    p = model[c];
    if( s > 5.393*p )
        break;
    code[c] = p;
    s += p;
}
for( auto& kv : code ) {
    char c = kv.first;
    code[c] /= s;
}

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é:

  1. Ajoutez le caractère correct au préfixe correct connu de Moby Dick.
  2. Mettez à jour le modèle (Markov) du texte.
  3. La sauce secrète : Si la supposition précédente était incorrecte, ramenez l’état du décodeur arithmétique à son état antérieur à la précédente!
  4. Demandez au modèle de Markov de prédire une distribution de probabilité P pour le prochain caractère.
  5. Transformez P en Q en utilisant le sous-programme de la section précédente.
  6. Demandez au décodeur arithmétique de décoder un caractère du reste du fichier d'indices, en fonction de la distribution Q.
  7. Devinez le personnage résultant.

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!


8
Wow, réponse impressionnante. Je suppose qu'il faut du code supplémentaire pour générer le Bfichier? Si oui, pouvez-vous inclure cela dans votre réponse?
Nathaniel

8
Excellent! La première (et à ce jour la seule) solution pour briser la barre des 500k.
ShreevatsaR

5
"dérive la merde", je pleure
Phill

5
Étant donné qu'aucune nouvelle réponse n'a été publiée pendant la période de prime, je l'attribue à votre réponse, à la fois comme étant la meilleure notation et l'approche la plus sophistiquée. Si vous avez le temps, j'apprécierais vraiment une explication plus détaillée du fonctionnement de cette réponse, à savoir quel est exactement l'algorithme?
Nathaniel

2
@Nathaniel: J'ai ajouté une explication à ce post. Faites-moi savoir si vous pensez que c'est suffisamment détaillé pour reproduire la solution vous-même.
A. Rex

77

Python 3, 2 · 267 + 510193 = 510727

Prédicteur

def p():
 d={};s=b''
 while 1:
  p={0:1};r=range(len(s)+1)
  for i in r:
   for c,n in d.setdefault(s[:i],{}).items():p[c]=p.get(c,1)*n**b'\1\6\f\36AcWuvY_v`\270~\333~'[i]
  c=yield max(sorted(p),key=p.get)
  for i in r:e=d[s[:i]];e[c]=e.get(c,1)+1
  s=b'%c'%c+s[:15]

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

with open('whale2.txt', 'rb') as f:
    g = p()
    wrong = 0
    a = next(g)
    for b in f.read():
        wrong += a != b
        a = g.send(b)
    print(wrong)

2
Le bon outil pour le travail. Excellent score. Joli.
agtoever

1
Clarification possible: 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.
Cœur

J'ai mis à jour la question avec une version du fichier où le texte n'est pas enveloppé - vous pouvez essayer de ré-exécuter votre code dessus (cela pourrait faire un peu mieux)
Nathaniel

3
Merci pour cette explication - si vous pouviez éditer un résumé dans la question, ce serait génial. (L’expérience de mon précédent défi, Paint Starry Night , c’était que ces procédures d’optimisation constituaient la partie la plus intéressante des réponses. Il est donc préférable que les réponses incluent le code utilisé pour le faire, et une explication. les défis en disant qu'ils devraient.)
Nathaniel

1
@Christoph Ma combinaison de modèles est en fait une moyenne géométrique pondérée. Mais la moyenne de PAQ dans le domaine logistique est légèrement différente - il faudra que je vérifie si c'est mieux.
Anders Kaseorg

55

Python 3 , 2 * 279 + 592920 = 593478 2 * 250 + 592467 = 592967 2 * 271 + 592084 = 592626 2 * 278 + 592059 = 592615 2 * 285 + 586660 = 587230 2 * 320 + 585161 = 585801 2 * 339 + 585050 = 585728

d=m={}
s=1
w,v='',0
def f(c):
 global w,m,v,s,d
 if w not in m:m[w]={}
 u=m[w];u[c]=c in u and 1+u[c]or 1;v+=1;q=n=' ';w=w*s+c;s=c!=n
 if w in m:_,n=max((m[w][k],k)for k in m[w])
 elif s-1:n=d in'nedtfo'and't'or'a'
 elif'-'==c:n=c
 elif"'"==c:n='s'
 elif'/'<c<':':n='.'
 if v>4*(n!=q)+66:n='\n'
 if s:d=c
 if c<q:w=w[:-1]+q;v=s=0
 return n

Essayez-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:

  • Si ce que l’on voit jusqu’à présent, c’est 'Captai', il prédit un "n"
  • Si c'est "Captain", il prédit un espace
  • Si c'est le début d'un mot et que le dernier mot était "Capitaine", cela prédit un 'A'
  • Si le mot jusqu'à présent est "A", il prédit un "h" (puis "a" et "b"; de même pour "C").

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:

nl tneund his    I woi tis tnlost ahet toie tn tant  wod, ihet taptain Ahab ses
 snd t
oeed Sft   aoid thshtego    Io, fhe soie tn tant  tot the soie      ahe sewbtoon
swn tagd  aoths eatmved fhe sewbtoon wor ta  I sfey  aote of totsonld nive betse
d ahe
hate Whale iorst  Ihe e ioi beaos! -there soi beaos! -there soi beaos!

ce qui correspond à cette partie de l'entrée:

tout autour de lui.

"Je l'ai vu presque au même moment que le capitaine Achab, monsieur, et j'ai crié", a déclaré Tashtego.

"Pas le même instant; pas le même - non, le doublon est à moi, le destin m'a réservé le doublon. Moi seulement; aucun d'entre vous n'aurait pu élever la baleine blanche en premier. Là, elle souffle! - là, elle souffle! - -là elle souffle!

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:

total = 0
right = 0
with open('whale.txt') as fp:
    with open('guess.txt', 'w') as dest:
        for l in fp.readlines():
            for c in l:
                last = c
                if p == c: right += 1
                n = f(c)
                p = n
                total += 1
                dest.write(n)
                if total % 10000 == 0:
                    print('{} / {} E={}\r'.format(right, total, total-right), end='')
print('{} / {}: E={}'.format(right, total, total - right))

guess.txt a la sortie devinée à la fin.


3
C'est une excellente approche!
Skyler

2
trop <s> </ s>;)
FantaC

1
+1 parce que cette approche m'a rappelé l'algorithme de compression LZW.
Marcos

25

C ++, note: 2 * 132 + 865821 = 866085

Merci à @Quentin d'avoir sauvé 217 octets!

int f(int c){return c-10?"t \n 2  sS \n  -  08........       huaoRooe oioaoheu thpih eEA \n   neo    enueee neue hteht e"[c-32]:10;}

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:

#include <iostream>
#include <fstream>

int f(int c);

int main()
{
    std::ifstream file;
    file.open("whale2.txt");

    if (!file.is_open())
        return 1;

    char p_ch, ch;
    file >> std::noskipws >> p_ch;
    int incorrect = 0;
    while (file >> std::noskipws >> ch)
    {
        if (f(p_ch) != ch)
            ++incorrect;
        p_ch = ch;
    }

    file.close();

    std::cout << incorrect;
}

Edit: Utiliser whale2.txtdonne un meilleur score.


5
Vous pouvez traduire ce tableau en un littéral de chaîne et le mettre directement à la place de Lpour sauvegarder un tas de caractères :)
Quentin

@ Quentin Merci! Maintenant, je me demande pourquoi je n'y avais pas pensé en premier lieu ...
Steadybox

20

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.

from collections import Counter as C, defaultdict as D
R,l=range,10
s,n='',[D(C) for _ in R(l+1)]
def A(c):
 global s;s+=c;
 if len(s)<=l:return ' '
 P=D(lambda:0)
 for L in R(1,l+1):
  w=''.join(s[-L-1:-1]);n[L][w].update([c]);w=''.join(s[-L:])
  try:
   q,z=n[L][w].most_common(1)[0];x=sum(list(n[L][w].values()))
  except IndexError:continue
  p=z/x
  if x<3:p*=1/(3-x)
  P[q]+=p
 if not P:return ' '
 return max(P.items(),key=lambda i:i[1])[0]
import this, codecs as d
[A(c) for c in d.decode(this.s, 'rot-13')]

Résultats:

Généralement du charabia, même si vous pouvez le voir reprendre une phrase occasionnelle, telle que "Père Mapple".

errors: 521122
TRAINING:
result:  tetlsnowleof the won -opes  aIther Mapple,woneltnsinkeap hsd   lnd the  thth a shoey,aeidorsbine ao
actual: ntal knobs of the man-ropes, Father Mapple cast a look upwards, and then with a truly sailor-like bu
FINAL:
result: mnd wnd round  ahe   ind tveryaonsracting th ards the sol ens-ike aeock tolblescn the sgis of thet t
actual: und and round, then, and ever contracting towards the button-like black bubble at the axis of that s

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.

from minified import A

def score(predict, text):
    errors = 0
    newtext = []
    for i, (actual, current) in  enumerate(zip(text[1:], text[:-1])):
        next = predict(current)
        errors += (actual != next)
        newtext.append(next)
        if (i % (len(text) // 100) == 0):
            print ('.', end='', flush=True)
    return errors, ''.join(newtext)

t = open('whale2.txt')
text = t.read()
err2, text2 = score(A, text)
print('errors:', err2)
print("TRAINING:")
print(text2[100000:100100].replace('\n', '\\n'))
print(text1[100001:100101].replace('\n', '\\n'))
print("FINAL:")
print(text2[121400:1215500].replace('\n', '\\n'))
print(text[121401:1215501].replace('\n', '\\n'))

3
Bienvenue sur le site! C'est une première soumission fantastique. :)
DJMcMayhem

@DJMcMayhem, Merci pour l'accueil. J'adore regarder depuis un moment maintenant, c'est le premier concours qui attire mon attention pour une entrée.
user2699

19

C (gcc) , 679787 652892

84 76 octets, 679619 652740 suppositions incorrectes

p[128][128][128][128];a,b,c,d;g(h){p[a][b][c][d]=h;h=p[a=b][b=c][c=d][d=h];}

Essayez-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é:

p[128][128][128][128];
a,b,c,d;
g(h){
    p[a][b][c][d]=h; // Memorize the last character.
    h=p[a=b][b=c][c=d][d=h]; // Read the guess. We save several
                             // bytes with the assignments inside indices.
}

... Le lien TIO est inutile. Donc, la fonction retourne la valeur de la dernière affectation?
user202729

Permettez-moi de modifier la réponse avec une explication, puis :)

1
@Rogem J'ai ajouté une version dépourvue de golf (ce que j'ai fait parce que je ne pouvais pas la suivre non plus) - espérons que cela ne vous dérange pas, mais veuillez revenir en arrière si vous le souhaitez.
Adam Davis

@AdamDavis sur la plupart des implémentations de C, toutes les variables globales commencent à zéro. C'est un comportement indéfini, il n'est donc utilisé que dans le code-golf.
NieDzejkob

1
@NieDzejkob Ah, tu as raison, merci! "ANSI-C nécessite que toutes les variables statiques / globales non initialisées soient initialisées avec 0."
Adam Davis

16

sh + bzip2, 2 * 364106 = 728212

2 * 381249 + 0 = 762498

dd if=$0 bs=1 skip=49|bunzip2&exec cat>/dev/null

suivi 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:

#!/bin/sh
if [ $# -ne 3 ]
then
    echo "Usage $0 gen.sh datafile output.sh"
    exit 1
fi

cat $1 > $3
dd ibs=1 if=$2 skip=1 | bzip2 -9 >> $3
chmod +x $3

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.

#!/usr/bin/tcc -run
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char **argv)
{
    volatile int result;
    int readfd[2];
    int writefd[2];
    int cppid;
    int bytecount;
    char c1, c2, c3;
    if (argc != 2) {
        printf("write X approximately -- service host\n");
        printf("Usage: %s serviceprocessbinary < source.txt\n", argv[0]);
        return 1;
    }
    /* Start service process */
    if (pipe(readfd)) {
        perror("pipe()");
        return 3;
    }
    if (pipe(writefd)) {
        perror("pipe()");
        return 3;
    }
    result = 0;
    if (!(cppid = vfork())) {
        char *argtable[3];
        argtable[0] = argv[1];
        argtable[1] = NULL;
        dup2(readfd[0], 0);
        dup2(writefd[1], 1);
        close(readfd[1]);
        close(writefd[0]);
        close(readfd[0]);
        close(writefd[1]);
        execvp(argv[1], argtable);
        if (errno == ENOEXEC) {
            argtable[0] = "/bin/sh";
            argtable[1] = argv[1];
            argtable[2] = NULL;
            /* old standard -- what isn't an executable
             * can be exec'd as a /bin/sh script */
            execvp("/bin/sh", argtable);
            result = ENOEXEC;
        } else {
            result = errno;
        }
        _exit(3);
    } else if (cppid < 0) {
        perror("vfork()");
        return 3;
    }
    if (result) {
        errno = result;
        perror("execvp()");
        return 3;
    }
    close(readfd[0]);
    close(writefd[1]);
    /* check results */
    read(0, &c2, 1);
    bytecount = 1;
    errno = 0;
    while (read(0, &c1, 1) > 0) {
        write(readfd[1], &c2, 1);
        if (read(writefd[0], &c3, 1) <= 0) {
            printf("%d errors (%d bytes)\n", result, bytecount);
            if (errno == 0)
                fprintf(stderr, "pipe: unexpected EOF\n");
            else
                perror("pipe");
            return 3;
        }
        if (c3 != c1)
            ++result;
        c2 = c1;
        ++bytecount;
    }
    printf("%d errors (%d bytes)\n", result, bytecount);
    return 0;
}

6
Je pense que ce qu'il demande, c'est: comment cela ne viole-t-il pas la 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?

8
@Rogem Les données compressées sont placées après ce qui est montré ici, et le code y accède.
user202729

4
La question dit: « Votre soumission sera un programme ou une fonction (etc.) qui sera appelée ou invoqué plusieurs fois quand il est appelé à la. nthTemps sera donné le nième caractère whale.txtou whale2.txtet il doit sa sortie estimation pour la (n+1)thpersonnage." - Comment cette exigence est-elle remplie? Le code affiche le texte entier de whale.txtchaque fois qu'il est exécuté.
axiac

1
@axiac "tout va bien, tant que votre programme donne toujours un octet de sortie avant de recevoir son prochain octet d'entrée."
user202729

5
@axiac étant donné le harnais de test, je suis heureux de considérer l'envoi du programme un octet de STDIN comme "l'appelant ou l'invoquant". L'importation est que le programme retourne un octet de sortie après chaque octet d'entrée, ce que fait celui-ci, lorsqu'il est exécuté à l'aide du faisceau de test. Comme le dit la question, "tout va bien, tant que votre programme donne toujours un octet de sortie avant de recevoir son prochain octet d'entrée".
Nathaniel

13

Python 3 , 879766

F=[[0]*123for _ in range(123)]
P=32
def f(C):global P;C=ord(C);F[P][C]+=1;P=C;return chr(max(enumerate(F[C]),key=lambda x:x[1])[0])

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:

  • augmenter frequency[prev][char]
  • Trouvez le personnage qui apparaît le plus souvent dans frequency[char]
  • et le sortir.

  • Code non-lié dans le lien TIO, commenté.
  • Le code est 131 octets.
  • Le code exécuté sur ma machine indique:
879504 / 1215235
Time: 62.01348257784468

qui ont le score total

2*131 + 879504 = 879766

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.


13

C #, 378 * 2 + 569279 = 570035

using System.Collections.Generic;using System.Linq;class P{Dictionary<string,Dictionary<char,int>>m=new
Dictionary<string,Dictionary<char,int>>();string b="";public char N(char
c){if(!m.ContainsKey(b))m[b]=new Dictionary<char,int>();if(!m[b].ContainsKey(c))m[b][c]=0;m[b][c]++;b+=c;if(b.Length>4)b=b.Remove(0,1);return
m.ContainsKey(b)?m[b].OrderBy(k=>k.Value).Last().Key:' ';}}

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.txtfichier, car elle améliore considérablement le nombre de suppositions réussies.

Voici le code utilisé pour tester la classe:

using System;
using System.IO;
using System.Text;

public class Program
{
    public static void Main(string[] args)
    {
        var contents = File.OpenText("whale2.txt").ReadToEnd();
        var predictor = new P();

        var errors = 0;
        var generated = new StringBuilder();
        var guessed = new StringBuilder();
        for (var i = 0; i < contents.Length - 1; i++)
        {
            var predicted = predictor.N(contents[i]);
            generated.Append(predicted);
            if (contents[i + 1] == predicted)
                guessed.Append(predicted);
            else
            {
                guessed.Append('_');
                errors++;
            }
        }

        Console.WriteLine("Errors/total: {0}/{1}", errors, contents.Length);
        File.WriteAllText("predicted-whale.txt", generated.ToString());
        File.WriteAllText("guessed-whale.txt", guessed.ToString());

        Console.ReadKey();
    }
}

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):

Size   Errors   Errors(2)
-------------------------
1      866162   865850
2      734762   731533
3      621019   604613
4      569279   515744
5      579446   454052
6      629829   396855
7      696912   335034
8      765346   271275
9      826821   210552
10     876471   158263

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:

"And did none of ye see it before?" cried Ahab, hailing the perched men all around him.

"I saw him almost that same instant, sir, that Captain Ahab did, and I cried out," said Tashtego.

"Not the same instant; not the same--no, the doubloon is mine, Fate reserved the doubloon for me. I only; none of ye could have raised the White Whale first. There she blows!--there she blows!--there she blows! There again!--there again!"

Recréé:

"Tnd tes note of to seamtn we ore  
sried thab  wedleng the srriead te  a l tneund tes  
"T day tim t lost shet toie tn tand  aor, ahet taptain thab sid  tnd t waued tnt   said teshtego  
"To, ahe shme tn tand  aot the shme whot nhe sewbteodsan tagd  althsteatnved the sewbteodsaor te, I hncy  aote of to sanld bave beised the shate Whale iorst  Bhe e ati boaos  -the   ati boaos  -the   ati boaos  the e anains -ahe   anains 

Suppositions:

"_nd ___ no_e of __ se____ _e_ore____ried _hab_ ___l_ng the __r___d _e_ a_l ___und _____
"_ _a_ _im ___ost _h_t ___e _n_tan__ __r, _h_t _aptain _hab _id_ _nd _ ___ed __t__ said __shtego__
"_o_ _he s_me _n_tan__ _ot the s_me___o_ _he ___b__o____ _____ __t___e___ved the ___b__o___or _e_ I _n_y_ _o_e of __ ___ld _ave __ised the _h_te Whale __rst_ _he_e ___ b___s__-the__ ___ b___s__-the__ ___ b___s_ _he_e a_ain__-_he__ a_ain__

Changer le journal

  • 569279 - remplacé whale2.txtpar l'optimisation ainsi supprimée.
  • 577366 - optimisé avec un code qui essayait de deviner quand renvoyer un saut de ligne.
  • 590354 - version originale.

4
Merci de montrer la variance lorsque vous modifiez la taille de la clé et le seuil de la colonne!
Jeremy Weirich

J'ai mis à jour la question avec une version du fichier où le texte n'est pas enveloppé - vous pouvez probablement enregistrer quelques points en l'utilisant
Nathaniel

@ Natalie il fait en effet. J'ai mis à jour la réponse.
Charlie

Vous pouvez enregistrer des octets en utilisant var au lieu de déclarer les types.
Ed T

1
Au fur et à mesure que la taille de la clé augmente, le nombre d'occurrences manquées diminue. Ainsi, davantage d'espaces sont générés lorsqu'une touche plus courte a pu deviner le bon caractère. Au fur et à mesure que la taille de la clé diminue, les suppositions individuelles sont moins précises pour les segments correspondants. Je soupçonne que c'est pourquoi une longueur de quatre est optimale. Si vous conservez des clés de plusieurs longueurs et utilisez des correspondances plus courtes lorsque les plus longues ne sont pas disponibles, le taux de réussite (et donc le score) sera considérablement amélioré pour les clés plus longues.
Jeffrey L Whitledge

11

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.

  • (1950 * 2 + 532919) 536819
  • (2406 * 2 + 526233) 531045 vérifiant la ponctuation pour mieux deviner
  • (1995 * 2 + 525158) 529148 plus de peaufinage, golfé quelques verbiage

package mobydick; import java.util.HashMap; public class BlindRankedPatternMatcher { String previousChars = ""; int FRAGLENGTH = 9; HashMap > patternPredictor = new HashMap<>(); void addWordInfo(String key, String prediction) { HashMap predictions = patternPredictor.get(key); if (predictions == null) { predictions = new HashMap(); patternPredictor.put(key, predictions); } WordInfo info = predictions.get(prediction); if (info == null) { info = new WordInfo(prediction); predictions.put(prediction, info); } info.freq++; } String getTopGuess (String pattern) { if (patternPredictor.get(pattern) != null) { java.util.List predictions = new java.util.ArrayList<>(); predictions.addAll(patternPredictor.get(pattern).values()); java.util.Collections.sort(predictions); return predictions.get(0).word; } return null; 
} String mainGuess() { 
if (trimGuess(",") != null) return trimGuess(","); if (trimGuess(";") != null) return trimGuess(";"); 
if (trimGuess(":") != null) return trimGuess(":"); 
if (trimGuess(".") != null) return trimGuess("."); if (trimGuess("!") != null) return trimGuess("!"); if (trimGuess("?") != null) return trimGuess("?"); if (trimGuess(" ") != null) return trimGuess(" "); for (int x = 0;x< previousChars.length();x++) { String tg = getTopGuess(previousChars.substring(x)); if (tg != null) { return tg; } } return "\n"; } String trimGuess(String c) { if (previousChars.contains(c)) { 
String test = previousChars.substring(previousChars.indexOf(c)); return getTopGuess(test); } return null; } public String predictNext(String newChar) { if (previousChars.length() < FRAGLENGTH) { previousChars+= newChar; } else { for (int x = 0; x addWordInfo(previousChars.substring(x), newChar); } previousChars = previousChars.substring(1) + newChar; } return mainGuess(); 
} class WordInfo implements Comparable { public WordInfo (String text) { this.word = text; } 
String word; int freq = 0; @Override public int compareTo(WordInfo arg0) { return Integer.compare(arg0.freq, this.freq); }

C'est un très bon score pour un langage aussi prolixe.
DJMcMayhem

1
Je pensais que cela valait le coup, car la taille du fichier laisse beaucoup à améliorer par rapport à la taille du programme.
Jim W

3
Ce n'est pas compilable sous Java 7 (ou toute version de Java, pour ce que ça vaut). Souhaitez-vous s'il vous plaît réparer votre code? Une fois que c'est fait, je jouerai volontiers au golf pour améliorer votre score.
Olivier Grégoire

Non testé, mais il devrait s'agir de votre code exactement le même: 950 octets . Cependant, votre code actuel contenait pas mal d'erreurs, donc je ne suis pas sûr d'avoir tout rempli correctement. Encore une fois, non testé, il suffit donc de comparer les versions pour voir ce que j'ai changé / renommé, et voir si tout fonctionne toujours de la même manière que votre code d'origine. Peut certainement être joué au golf un peu plus, cependant.
Kevin Cruijssen

Merde, je l'ai fait tout en m'ennuyant à mon ancien travail et je n'ai pas pris le code avec moi. Je vais devoir jeter un coup d'oeil pour voir où est la faute de frappe.
Jim W

10

Python 3, 2 × 497 + 619608 = 620602 2 × 496 + 619608 = 620600

import operator as o
l=''
w=''
d={}
p={}
s=0
def z(x,y):
 return sorted([(k,v) for k,v in x.items() if k.startswith(y)],key=o.itemgetter(1))
def f(c):
 global l,w,d,p,s
 r=' '
 if c in' \n':
  s+=1
  if w in d:d[w]+=1
  else:d[w]=1
  if w:
   if l:
    t=l+' '+w
    if t in p:p[t]+=1
    else:p[t]=1
   n=z(p,w+' ')
   if n:g=n[-1];l=w;w='';r=g[0][len(l)+1]
   else:l=w;w='';r='t'
 else:
  w=w+c;m=z(p,w)
  if m:
   g=m[-1]
   if g[0]==w:
    if s>12:s=0;r='\n'
   else:r=g[0][len(w)]
 return r

J'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 ts'il n'y a pas de correspondance. Ce n'est pas très intelligent, cependant. Ensuite Moby, le programme devine correctement que le caractère suivant est D, 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 \ntoutes 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:

T t t t t t t t t L t t t tsher t t t ty t to t t te t t t t t tem t t t d b ta tnL te t tv tath a to tr t tl t l toe g to tf ahe gi te we th austitam ofd laammars, tn te to t tis nf tim oic t t th tn cindkth ae tf t d bh ao toe tr ai tat tnLiat tn to ay to tn hf to tex tfr toe tn toe kex te tia t l t l ti toe ke tf hhe kirl tou tu the tiach an taw th t t Wh tc t d t te the tnd tn tate tl te tf teu tl tn oan. HeAL. tn nn tf r t-H ta t WhALE.... S tn nort ts tlom rhe ka tnd Dr t t tALL th teuli th tis t-H taCTIONARY " t r t o t a t A t . t eALT t I t HLW t I t e t w t AO t t t AOLE, I T t t t ALE t w t t R t EK t T t R tSupplied by wnLw t t iit ty cce thet whe to tal ty tnd

mais à la fin, ça ressemble un peu plus à ... quelque chose. Mon passage préféré de la fin du livre,

et comme aucun des deux ne peut être à moi, laissez-moi alors déchirer en lambeaux, tout en vous poursuivant, bien que vous soyez lié à toi, maudite baleine! AINSI, j'abandonne la lance! "

sort comme

I dhrnery oyay ooom the woc Ihal iiw chshtego -tit my ti ddohe bidmer Hh, ho sheee opdeprendera toetis of tygd ahesgapdo tnep tnd tf y arosl tinl ahesgaorsltoak, and tidlhty ai p, cnd telas taep toip syst ho she tachlhe tnd tith ut ay Rnet hor bf toom the wist tord oaeve of ty nsst toip recked,hontain th, tingly toadh af tingly tike 'h, tot a hoet ty oh ost sreat ess iik in ty oh ost sremf Hew hiw"aoom tnl tou oolthert tyand . taoneoo sot an ao syad tytlows of ty oii e oor hoi tike and th ohes if oaped uoueid tf ty ooadh Ih ards the t houle lhesganl p tyt tpdomsuera tiile ah the wist t hrenelidtith the Ioom ti p s di dd o hoinbtn the Ior tid toie o hoetefy oist tyoakh on the Opr tnl toufin and tnl ti dd .mh tf ooueon gaor tnd todce tovther lon by tygd ait my the th aih tapce ciice toill moaneng she thesgh thmd th the thesgaoy d jiile YhE t hrve tpothe woerk "

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

#! /usr/bin/env python3
import sys
import os
import mobydick as moby


def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

total = 0
right = 0
real_char = ''
guess_char = 'T'
print('T',end='')
with open("whale.txt") as whale:
    while True:
        if real_char == guess_char:
            right += 1
        real_char = whale.read(1)
        if not real_char:
            eprint(str(right) + " / " + str(total) + " (" +
                str(right/total*100) + "%)")
            size = os.path.getsize("mobydick.py")
            eprint("Source size: " + str(size) + "B")
            eprint("Score: " + str(2*size + total - right))
            sys.exit(0)
        guess_char = moby.f(real_char)
        print(guess_char,end='')
        total += 1

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.


2
Bienvenue chez PPCG!
Martin Ender

1
Ne serait pas lambda i:i[1]moins cher que de traiter avec operator?
Draconis

@Draconis Presque certainement.
georgewatson

9

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 Cest 60661 octets, donc dans les règles de notation fichiers multiples , nous marquons Len 2167 + 60661 + 1 = 62829.

Le programme prend environ 8 minutes pour s'exécuter sur une m5.4xlargeinstance 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.)

#include <map>
#include <queue>
#include <vector>
using namespace std;

FILE *in;
unsigned int a, b = -1, c, d;
string s, t;
double l, h = 1, x[128][129], y[129], m[128];
map<string, int> N;
map<string, double[128]> M;
int G, S;

int f(int C)
{
    int i, j;
    for (i = 0; i <= 20 && i <= S; i++) {
        t = s.substr(S - i);
        N[t]++;
        M[t][C]++;
    }
    s += C;
    S++;

    for (i = 0; i < 128; i++)
        m[i] = 0;

    int E = 0;
    for (i = 20; i >= 0; i--) {
        if (i > S)
            continue;
        t = s.substr(S - i);
        if (i <= 2 && E >= 100 && (i == 0 || t[0] != ' '))
            break;
        if (M.find(t) == M.end())
            continue;
        for (j = 0; j < 128; j++) {
            m[j] += M[t][j] / N[t];
        }
        E += N[t];
    }

    double r = 0;
    for (i = 0; i < 128; i++)
        r += m[i];
    for (i = 0; i < 128; i++)
        m[i] = m[i] / r;

    if (!in) {
        in = fopen("C", "r");
        for (i = 0; i < 4; i++)
            c = c << 8 | getc(in);
    } else {
        l = x[C][G]
            + (l - y[G]) * (x[C][G + 1] - x[C][G]) / (y[G + 1] - y[G]);
        h = x[C][G]
            + (h - y[G]) * (x[C][G + 1] - x[C][G]) / (y[G + 1] - y[G]);
    }

    priority_queue<pair<double, int>> q;
    for (i = 0; i < 128; i++) {
        q.push(make_pair(m[i], i));
    }

    int n = 0;
    double s = 0;
    while (q.size()) {
        i = q.top().second;
        q.pop();
        if (m[i] < s / (n + 15))
            break;
        s += m[i];
        n++;
    }

    r = 0;
    for (i = 0; i < 128; i++) {
        y[i + 1] = m[i] - s / (n + 15);
        if (y[i + 1] < 0)
            y[i + 1] = 0;
        r += y[i + 1];
    }
    for (i = 0; i < 128; i++)
        y[i + 1] /= r;

    for (i = 0; i < 128; i++) {
        r = 0;
        for (j = 0; j < 128; j++) {
            x[i][j + 1] = y[j + 1];
            if (i == j)
                x[i][j + 1] *= 16;
            r += x[i][j + 1];
        }
        for (j = 0; j < 128; j++)
            x[i][j + 1] /= r;
        x[i][0] = 0;
        for (j = 0; j < 128; j++)
            x[i][j + 1] += x[i][j];
    }

    y[0] = 0;
    for (i = 0; i < 128; i++)
        y[i + 1] += y[i];

    for (G = 0; G < 128; G++) {
        if (y[G + 1] <= l)
            continue;
        if (y[G + 1] < h) {
            d = a + (b - a) * ((h - y[G + 1]) / (h - l));
            if (c <= d) {
                b = d;
                l = y[G + 1];
            } else {
                a = d + 1;
                h = y[G + 1];
            }
            while ((a ^ b) < (1 << 24)) {
                a = a << 8;
                b = b << 8 | 255;
                c = c << 8 | getc(in);
            }
        }
        if (h <= y[G + 1])
            return G;
    }
}
// End submission here.  Test code follows.
int main()
{
    FILE *moby = fopen("whale2.txt", "r");

    int E = 0;
    int c = getc(moby);
    while (c != EOF) {
        int guess = f(c);
        c = getc(moby);
        if (c != guess)
            E++;
    }

    printf("E=\t%d\n", E);

    return 0;
}

7

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".

from collections import*
D=defaultdict
M=[D(lambda:D(int))for i in range(10)]
X=""
def f(c):
 global X;G=D(int)
 for L in range(10):
  M[L][X[:L]][c]+=1;N=M[L][(c+X)[:L]]
  if N:g=max(N,key=lambda k:(N[k],k));G[g]+=N[g]*L**8
 X=(c+X)[:10]
 return max(G,key=lambda k:(G[k],k))

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:

from collections import defaultdict

PATTERN_MAX_LEN = 10
prev_chars = ""
patterns = [defaultdict(lambda:defaultdict(int))
            for i in range(PATTERN_MAX_LEN)]
# A pattern dictionary has entries like {" wh": {"i": 5, "a": 9}}

def next_char(c):
    global prev_chars
    guesses = defaultdict(int)
    for pattern_len in range(PATTERN_MAX_LEN):
        # Update patterns dictionary based on pattern and c
        pattern = prev_chars[:pattern_len]
        patterns[pattern_len][pattern][c] += 1
        # Make a guess at the next letter based on pattern (including c)
        pattern = (c + prev_chars)[:pattern_len]
        if pattern in patterns[pattern_len]:
            potential_next_chars = patterns[pattern_len][pattern]
            guess = max(potential_next_chars,
                        key=lambda k:(potential_next_chars[k], k))
            frequency = potential_next_chars[guess]
            # Exact formula TBD--long patterns need to be heavily
            # advantaged, but not too heavily
            weight = frequency * pattern_len ** 8
            guesses[guess] += weight
    # Update prev_chars with the current character
    prev_chars = (c + prev_chars)[:PATTERN_MAX_LEN]
    # Return the highest-weighted guess
    return max(guesses, key=lambda k:(guesses[k], k))

Et le harnais de test:

from textPredictorGolfed import f as next_char
# OR:
# from textPredictor import next_char

total = 0
correct = 0
incorrect = 0

with open("whale2.txt") as file:
    character = file.read(1)
    while character != "":
        guess = next_char(character)
        character = file.read(1)
        if guess == character:
            correct += 1
        else:
            incorrect += 1
        total += 1

print("Errors:", incorrect, "({:.2f}%)".format(100 * incorrect / total))

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).

 you take in hand to school others, and to teach them by what name a whale-fish
xU wshhlnrwn cindkgo dooool)tfhe -; wnd bo so rhoaoe ioy aienisotmhwnqiatl t n 

Vers la fin, il y a encore beaucoup d'erreurs, mais aussi de très bonnes séquences ( shmage seashawkspar exemple).

savage sea-hawks sailed with sheathed beaks. On the second day, a sail drew near
shmage seashawks wtidod oith tua dh   tyfr.  Tn the shaond tay, wnltiloloaa niar

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 fois o- pour sailor, je présume. Ou encore, après , aavoir nattendu - peut-être à cause de la fréquence courante de , and.


Changelog:

  • 274 * 2 + 526092 = 526640 Golfé l'algorithme, au prix de quelques erreurs supplémentaires
  • 306 * 2 + 526089 = 526701 Version originale

6

Python 2, note: 2 * (407 + 56574) + 562262 = 676224

Recherches pour les mots qui correspondent aux caractères précédents dans une liste de  tous  les mots les plus utilisés dans le texte, triés par le nombre de leurs occurrences.

Code:

import zlib
f=open("d","rb")
l=zlib.decompress(f.read()).split()
w=""
def f(c):
 global w
 if c.isalpha():
  w+=c
  try:n=next(x for x in l if x.startswith(w))
  except StopIteration:return" "
  if len(n)>len(w):
   return list(n)[len(w)]
  return" "
 w="";
 n=ord(c)
 if n>31:
  return list("t \n 2  sS \n  -  08........       huaoRooe oioaoheu thpih eEA \n   neo    enueee neue hteht e")[n-32]
 return"\n"

Données: https://www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0

Suite de tests:

incorrect = 0

with open("whale2.txt") as file:
    p_ch = ch = file.read(1)
    while True:
        ch = file.read(1)
        if not ch:
            break
        f_ch = f(p_ch)
        if f_ch != ch:
            incorrect += 1
        p_ch = ch

print incorrect

Edit: Utiliser whale2.txtdonne un meilleur score.


5

C ++ (GCC), 725 × 2 + 527076 = 528526

Encore une autre soumission préfixe-fréquence. Courez dessus whale2.txtet obtenez un score similaire (légèrement pire) que les autres.

#import<bits/stdc++.h>
char*T="\n !\"$&'()*,-.0123456789:;?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz";
int I[124];std::string P(7,0);struct D{int V=0;std::array<int,81>X{{0}};};std::vector<D>L(1);D
init(){for(int i=81;i--;)I[T[i]]=i;}int
f(int c){P=P.substr(1)+(char)I[c];for(int i=7;i--;){int D=0;for(char
c:P.substr(i)){if(!L[D].X[c]){L[D].X[c]=L.size();L.push_back({});}D=L[D].X[c];}++L[D].V;}std::vector<int>C(81);for(int
i=81;i--;)C[i]=i;for(int
i=0;i<7;++i){int D=0;for(char c:P.substr(i)){D=L[D].X[c];if(!D)break;}if(!D)continue;int M=0;for(int
x:C)M=std::max(M,L[L[D].X[x]].V);C.erase(std::remove_if(C.begin(),C.end(),[&](int
x){return L[L[D].X[x]].V!=M;}),C.end());if(C.size()<2)break;}return T[C[0]];}

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îne abcdefghiet abcdefghjapparaît avec la plus grande fréquence dans toutes les chaînes de la forme abcdefgh*, la sortie sera soit iou j, 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:

int main() {
    init(); 

    std::cout << "Start ---\n";
    std::time_t start = std::clock();

    std::ifstream file {"whale2.txt"};
    // std::ofstream file_guess {"whale_guess.txt"};
    std::ofstream file_diff {"whale_diff.txt"};
    if (!file.is_open()) {
        std::cout << "File doesn't exist\n";
        return 0;
    }

    char p_ch, ch;
    file >> std::noskipws >> p_ch;
    int incorrect = 0, total = 0;
    // file_diff << p_ch;

    int constexpr line_len = 80;
    std::string correct, guess_diff;
    correct += p_ch;
    guess_diff += '~';

    while (file >> ch) {
        char guess = f(p_ch);

        // file_guess << guess;
/*        if (guess != ch) {
            if (ch == '\n') {
                file_diff << "$";
            } else if (ch == ' ') {
                file_diff << '_';
            } else {
                file_diff << '~';
            }
        } else {
            file_diff << ch;
        }*/
        incorrect += (guess != ch);
        total += 1;
        p_ch = ch;

        if (guess == '\n') guess = '/';
        if (ch == '\n') ch = '/';
        correct += ch; guess_diff += (ch == guess ? ch == ' ' ? ' ' : '~' : guess);
        if (correct.length() == line_len) {
            file_diff << guess_diff << '\n' << correct << "\n\n";
            guess_diff.clear();
            correct.clear();
        }
    }

    file_diff << guess_diff << '\n' << correct << "\n\n";

    file.close();
    file_diff.close();

    std::cout << (std::clock() - start) 
    / double(CLOCKS_PER_SEC) << " seconds, "
    "score = " << incorrect << " / " << total << '\n';
}

Ungolfed:

size_t constexpr N = 7;

int constexpr NCHAR = 81;

std::array<int, NCHAR> const charset = {{
'\n', ' ', '!', '"', '$', '&', '\'', '(', ')', '*', ',', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', ']', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
}}; // this actually contains a lot of information, may want to golf it
// (may take the idea of using AndersKaseorg's algorithm, late acceptance hill climbing)

std::array<int, 'z' + 1> const char_index = [](){
    std::array<int, 'z' + 1> char_index;
    for (size_t i = NCHAR; i --> 0;) 
        char_index[charset[i]] = i;
    return char_index;
}(); // IIFE ?

std::string past (N, 0); 
// modifying this may improve the score by a few units

struct node {
    int value = 0;
    std::array<size_t, NCHAR> child_index {{0}};
};
std::vector<node> node_pool (1); // root

int f(int c) {
    past = past.substr(1) + (char) char_index[c];

    for (size_t i = 0; i < N; ++i) {
        // add past.substr(i) to the string
        size_t node = 0;
        for (char c : past.substr(i)) {
            if (node_pool[node].child_index[c] == 0) {
                node_pool[node].child_index[c] = node_pool.size();
                node_pool.emplace_back();
            }
            node = node_pool[node].child_index[c];
        }
        assert(node != 0); // the substring is non-empty
        ++node_pool[node].value;
    }

    std::vector<size_t> candidates (NCHAR);
    std::iota(candidates.begin(), candidates.end(), 0);
    for (size_t i = 0; i < N; ++i) {
        size_t node = 0;
        for (char c : past.substr(i)) {
            node = node_pool[node].child_index[c];
            if (node == 0) break;
        }
        if (node == 0) continue;

        assert(node_pool[0].value == 0);
        int max_value = 0;
        for (size_t x : candidates)
            max_value = std::max(max_value, node_pool[node_pool[node].child_index[x]].value);

        candidates.erase(
            std::remove_if(candidates.begin(), candidates.end(), [&](size_t x){
                return node_pool[node_pool[node].child_index[x]].value != max_value;
            }), candidates.end()
        );

        if (candidates.size() == 1) 
            break;
    }

    return charset[candidates[0]];
}

Exemple de sortie:

~ ~s  ta~ hard ts tt~~~~~~~ ~doam ~~ ar~ ~ i~~~ ~~~ ~he~~~~,a~ t~~~~ t~ ho~si~  
n--as his wont at intervals--stepped forth from the scuttle in which he leaned, 

~~~ thr~ ~~ t~~ crp~~~~~~~~ a~ wap~~~~~ a~eo~~ h~~ o~~ s~~~ or~~y~ ~  boog~e~~ t
and went to his pivot-hole, he suddenly thrust out his face fiercely, snuffing u

~ a~~ ~h~ ~n~ onitn~oi~~~~~~ ~~a~ ~ cewsoat~  a~ tae~~~~ ~e~~t~~ te~~ ouc~s~i~~ 
p the sea air as a sagacious ship's dog will, in drawing nigh to some barbarous 

ct as I~ iisk~~~~ ~~e~ tls~~~~ i~~~ ~~ soe~e Ae ~ ~~e~ tar~~~~~ trd~  ot ~ h~~~ 
isle. He declared that a whale must be near. Soon that peculiar odor, sometimes 

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)

 au t  tf weu~i~ aor~ mre~g~~~ m~t~~ ~~~  ~"NC~X~t~ti~  ~~n~ SNsh A FNECnSERTR O
 on as it rolled five thousand years ago./////Epilogue//"AND I ONLY AM ESCAPED A

NL~~,S~ ~HR~ yO~ -/s~n "~A~~ laeu~ta Vew~, S~e s~~  s~ ~ ain~ t~d ~t~ oirept~~ ~
LONE TO TELL THEE" Job.//The drama's done. Why then here does any one step forth

Les majuscules ne semblent pas bonnes.


Trie semble être plus
gourmand en

... et aussi plus difficile à mettre en œuvre.
user202729

4

Python 2, 756837

Utilise quelque chose qui pourrait être des chaînes de Markov?

import zlib
a=eval(zlib.decompress('x\x9cM\x9cis\xda\xcc\xd2\x86\xff\x8a2\xf5\xd4\x81\xb8,\x977l\'\xf9\x90\x12 \x02f\x11G\x02c||*%@,a\x11a1\xe0S\xef\x7f\x7fC\x13\xf75\xdf\xda\xaaa4\xd3\xcb\xddw\xf7\x8c\xfc\xbf\xcc\x8f\xd7E\xe6\xab\x93if\xce\x9d\xcc\x8f\xefG\xd1\x11\xf1\x1b\xa2At\x8e\xa2\'\xe2\xc5Q\xfc,\xa2{\x14+"\x9e3\xf63b\x87\x9f\xb5\x8fb$b\xeb(\x96E\x8c\x18\x1b2\xb6{\x14/D\xfcq\x14\x03\x11}\xc6zG\xb1.b\xc0\xd3\x06\xcb\xa9\xf1\xb3\xcaQl\x88X>\x8a-\x11\xb7G1\x11q\x85\x98\x1c\xc5\x95\x88\xf1Q\xec\x89\x98\x1e\xc5\x81\x88\xa2\xb3X\xc4\x19\xe2\xe4(\xbe\x898\xd6\xc9F\xa8\xe4E\x16\x19\x8a\xc8r^|U\xc9\x8b\xc7\xd8\xfcQ\xf4\x8f\xe2\xbf\x1c\x06\xbc\xa8v6\xef\xba\xb2\x17V\xf6\x92\xe8r6\x07\x9d\xcc\x95EN\xe4\xe9FW\xb6\xd9\xea6M\xa2K\xdf\xact\x86\xf9\xc976Gy\xf2\xce\xef\x96G1\x15q\xf1\xf1\xd4\xcc3\xe6\x8f\xb8\x96\xdf}\xd27\xcf\x1d\x9da\x8e\x1f\xcd\xc5c\\\x11Q\xcf\xfc\x02Q\x9c\xe7\\\xd6\xbe;\x8acY\xe5\x8c\x17\xcfu9F\xc4\x83\xfc\x0c\x076\x0b\x1d;\xc7\x97\xe7_U\x9c\xacT\xfc\xc2\x1a\xbe\xb0\x06\x83\r7b\xd9\x85<\x9d\xe8\x86\xbe|Q\xff\xfc\xf2\xa0\xe2d\xa7?\xfbr\xc5\xbc\x97\x8c\xbd\xd1\xbd}\xb9f@\x8e\x01\xb7\x88\xf7\x88w*\xce\x13v1\xc1ZCv\x1c\xebz\xe7=]\xce\x1c\x9d\xcdg\xe8,U/\x98/\x18`\xed\xf8\x8d\xa7\xe21\'\x1bo\xd4,sk\x80\xb8\xc6L\xc45Oq\xa9M\xac\x9e8\xc7?k\xb8\x9fY\xe9\x80\x9a\x8c\x9d\x8a\x98\xea\xde\x8c\xcc\xbb\x94\xa7\x13\x06\xc8\xca\xfa"\x1e\x98\xa1\xa4\xe1R\xfb\xa1\xb1W+\xf2b\xc0\xa4\x96W\xac\xa8\x15\x10=\x8d\xd3ZC#\xb2F \xd7j\xccP\xd78\xadU\x8fbWD"\xbd\xd6Q\xb7\xaf\xb5\x98\x0cH\xac\x85\xfc\x0cH\xac5\x15(k\xdd\x8f\xa7\xa6&\xf1v\xfa\x19\x00Q\xc3\x7fkxuM\xe2\xad(\xa2D\xd6\xabX\xb6&\xfeyy\x14\x1d\xdc\xa4v\x8azY\xdbU\xa4P\xf9\xc4\xcc?\x0fj\x8d\x9f\x135\xf8O\xde\xf7\xd3Q?Ym\xf4\xe9\n\xefY\xe12\xab\x9d:\xc7\n`Y\xfd>\x8a[\x11\xf1\x88\xd5\x9a\xc9\xf6\xcc\x80#\xad\xde\xd5+W\x03\x9e\x12/\xab!\xf3\x8e\x98\x81xY\xf5\x18\xd0g2\xe2e5g\xb2\x05+\x13\x07\x9d\x8b8fCD\xd1j\xca\xcf,X]\x81X+\xb0i\xa5\x88\xf5\'\x1c\x14VW`\xe9\n\x84]\x19u\xaa\x15\x16X\x81\xb0+\x0c\xb7"\'\xbf.N\xab0\xa7?n\xd5\x13^\x179\xb5\xf9\xebB<\xe4\xe1$_[c\x04\xc3\x06\'\x99W\xbd.\xb2\x1ap\xaf\x8b\xb3\x8fy\xcc\x9fW\x19\xe6t\xacE\x18\x1d\xffoR\xf1\xeb\xa2k\xc9/\x96\xfc\x1fk\xfa\x96Z\xe7u\xd1VLx]<\xa9Q^\x17\x1dkL\xd3\x9a\xe7\xdfj\xe4\xd7Eh\x8d\x8fT\xc3\xaf\x8b\x9a5\xben\xc9\ru\xd2\xd7E\xa0\xf6}]\x94\xad1\x15k\x8b\x8f\xd6\xf8\xaa\xf5\xae\xa25\xde\xb7\xe6)Y\xe3\x7fX\xb2g\x8d\xc9[\xeb/(:\xfc[\xd4P9=>X?}\xb7\xe4\x8d\xa5\x92\xad5\xe5\x9b\xb5\x9c\x9d5Fbru\x92\x7f[\xaf]Y\xe3\xd7\x96\xdaf\xd6\x16\xe7\x1a\t\xaf\x8b\x85\xb5\x06\t\x96\xe1I\x1e[\xf3L\xac\xf5\xfc\xb2~;\xb5\x9e\x0f\xac\xf1\x12\xd7\xfb\x93<\xb4\xe6\x1fYk\x8e\xad\xdf\xf6\xac\xdf\xf6u\xfc\x80\x00\x19\x10A\x03\xdcz\xa0ac\x06\x84\xe3\x00>3 2\x07D\xe6\x80\xd8\x1e\x10\xdb\x03\xd8\xc8\xc0\x02\x82\x01\xb9w \xea\xd9\x89\x08\xee\x0c\xe6\xaa\xd8\x01\xba\x19L\xf9\x19\x9a\x1c\xa0\xc8\x01\x807\x00\xf0\x06hq\x00\xd9\x1d\xf4\xd0\x89\xa5\x9e\x985\x80\xb4\x837\xd6\x00\x82\x0f\xf0\xae\x01\x19y\x80\xaf\x0c@\xf0\xc1\xf2cCf\x87Vw\xe8o\x87Vw\x98h\x87]vXk\x07a\xdc\xa1\xf6\x1d\xba\xdea\x81K\x012aR\x977\x88\x97\no\x97W<\x85u]\n\x17;e\xceK(\xda%\xc4\xed\x12\x16x\t7\xdcYV\xbe\x94-I\xba\xbcd\xa3\x97\xec\xee\xf2\\W\xb1\xc3r;l\xb4\xc3r\xbb\xbe\xea}\xd7C\x14s\x9dt\t\xb5\xdb-\xd0\x04>\xb5#)\xed\xe0\xb5;\x12\xd8\x0e\x84\xd8Q8\xec0\xe2\x8e\xe4\xbc[2\x00?\xb9\xc4#\nl\xb3\x80\xe5\n\xa2\x12![\x05\x81G!\x1e\x05AP)\xed\n\x02\xac\x02\xfa\x85\x80\xa75\xc5\xba\x02t\xad  )\xc5l\x01jW\xe8"\x86\xbcB\xd0RrR\xa1\xc5+\x08\x9d\xc2X\xd5W \xbd\x17f\xba\xcd\x82\xa8Z\xd2N!Q\xf5\x15\xdeU}\x85\x83\xc6@a\xa5\x01U\x10\xa5\x9e\xd8\xee@\x9fN 4\x06,3#\xd5\xaf\x01\xc9\x0c$\xc5\x10\xa8\x13\xe0y\xb2\xd4\x1dO0\x96I\xd5\x16\x93\xadnh\x82\x85\xcc/f \x1f\x18\x06L\xc6\xba\x9c\t\xc8c\xc8\x17\x13j\x8c\xc9L}}\x92\xea\xd2\'\xe2\x88#\x11\xd9\xd0\x04\xaa5\xe9\xf1\xb3D]\xd9\x90\xce&#\xc6\x0e\xd9[\x11\x9d\xf9\xe8\x97dj\xc8\xa5\xc6\xd3\x080dRSP\xbb\x99\x1ac\xeb<%\xf3\x9b\x00\x9d\x91\xf7\ri\xdf<2/I\xdf\xc0Y\x0c\x94\xc5<1\x03\x84\xc5\xc0W\x0ct\xc5\x84,\x07\xb2b\xe0KO\xb2\xb7\x9ah\x07\xf43\xaf\x19uv\x039\x7f\x12MI\x1d\xf3$k/\xc8\x80\x0b\xc5.s\x06\xe6=\xc9\x9e\xa58\x99\xb8\xea\xd7\x13"yr\x81\xed\x01\xb7\x89\xbcN\xb2\xd9\xc4\xe8l\x7f\xcah\x85|\xc3:\x9fp\x89\'0\xefi\xa2\xa29\x81\xe9\xdf\x15\xa5j\xc7\xc9\xe9\xb9\xbc&Gc)\x87\xeb\xe6@\xe4\x1c8\x9d\xcb)\xde\xe6\xc0\xf4\x1cew\x8e\x04\x90#-\xe4.u\xc99RHN\x12\x8b$\xa1\x1cj\xc9\x01{9\xf8w\x19L*\xd3\xf2*S\xf5\x95\x9fxJ\xff\xac\xdcb\x00uc\xb9\x82\xd8`\x00Uj\xb9\xce\x0c@d\x19\x88,\x1f\xd4ve\xca\xb4\xf2\x04\x11RR\x8e\xd5\x1ce*\xab\xb2m\x992&-\x7fV\xfd\x94/\xac\x11(\xa8\xec\xaac\x95\xb5\x92\xfd\x13VZ\xdf\xfeG\xb4\xd2\x16Q;d&\xf3\xcd\xe8l\xaf\x19\xcb\xb52\xce\x87k\x99\x8c{\x14]\x11\xcf\xcd\xc7\x0b\x17$8\x8br.\x00\xbf\x05yqA\xb6\xb4\xe8\xec\x02\xb6v"\xb3\x12\x86\'\xaey\x12\xa1R\'\xa6y\x1aKM\xba@s\'\xea*\x00qb\xae\xa7\xa7{\x9e\x92N\x17$\x97/\x04\x96E\xd2-\x8enQ\xf4\x05I`AA\xbe \tX\xf4\x7f\xa1t\xcedv\xe6o\xf8\x98\xcc\x9b\xf9;\xc0d\xb6\xe6\xef6Mf\xf3\xa1T\x93Y#\xae\x18\xfb\xdb\xfc]\x8e\xc9,\x8d\xce{`\xc0\x88\xa7C\xf3Wg&\x93\x98\xbf+3\x7fx\xb6\xce\xdb?\x8a3\x11{\xcc\x1b36\xe5\xe9\xe2\x8fh2\xe6(\xce\x99a\xc6\x0c\x13\xf3\xd7\xf2&3f9\x1dv\xfc\xc4\xd3\x16O#\xdc\x08&\xba\xb8\xc0-\x9bFm\x01\x81]\x00\x88\x0b\xc3\xd8\xae\xbe\xe2T!\x9f\x94\xea\x1f\xc5\xbd\x88E\xb4S@\xcc\xb3M\xcf\xa8{~g\xde\x80\xf56\xf8Y\xfdc\xac\xc9\xd4\xcc_\xe72\x99\n\xda)\x7f\x8c\xcd|eo_\x1du\xb9\xaf\xf4\x1a\xbeZ\xe1\xfe\'Gj\xac\xd6\x8f\x1b\x15\xbdg\xea\x8e\xe6\x9c:\xd3\xd5\t\xfc:\xc8X\x07%\xea\xf0\xf7\xfa\xe9%\x1d\x91\xe9l\xd7\xc9\x12u\x89>\xe9\x82\xd7\x01\xab:\xb5G}\xc3\xc4+D"\xaa\x0e\x08\xd6i\xf6\xd5\x0b\x9a\x0e\xeb4\x06\xeb\x02\xa3\xc2\x1e\xeb5\x05\xad:8[o(\xce\xd6+\xec\xbe\xcd\xcf\x9a\ne\xf5\x88\xe5\x90\x0c\xce_9[X[\x95\xc3\x1aD]S\xca\xac\xd1\xd59f:G\xdb\xe7g\x0c \xf9\x9c\xd3\xeeYgu\x99k\xcc\xb1f\x865\xf6ZS\xf1\xae\xf1\xe7\xb5z\xb9Yg48\xce\x1f\xf4\x15\xdfu2\xf3\x9d\x01\xdfA\xec\xccwG\xcd\xbc\xc62k@kM\x07y\r\xc0\xad\xa98\xd6t\xdd\xd7\x18\x7f\r\xd6\xad\xa1\xab\xeb_\x8a\xcdk\xe0\x7f\r\xb5]\xc3\xf6\xd7\x00\xfd\x1a\xf8_\x93\x14\xd6}\x85\xdeu\x8f\xa7\xb4\xb9\xd7#\xd6\x0b\xd0\xaf\x81\xff55@H\xb9\x15&\xba\x86P&\x93f[\xc8\xca\xc2\xb1\xbe-\x94]\x08\xa7\x0e\xe1\x07!\xdd\xa0\xf0\tQ\xb8\x84\x90\xa3\xb0\xa9\x8e\x1dBAB(H\x88[\x86\xf4\xccC\x02&\xfc\xa1\x8e\x1dz\x1a0a^}<\xa49\x15R\xb0\x85\xb0\x91P\x02F\x90#\xa4\xb8\x0b\xe9\x99\x87\xd4\x84!\xce\x1e\x12\x02!\xbd\xd2\x10\x18\n\xc5\xa3\xaeD\xc4\x81C\xf1\xc4\xbc\x888{\x08\xf6\x84\xa7\x88\x93pH(e\x12J\x99$Us&\xd4\xd4\t\x0c5\xa1\r\x93L\x15\x91\x12|.I\xd4\xc8\t| !\xf3\'\x94\x7f\tT+\xe9+\x16$\x90\x8b\x84pI\xf6\x0c\xe0\xb0.\x81\xcd%DC\xb2C$\xf3\'\x84VB\x01\x99\x10\x86\tgf\xc9\xcf\xa3(\\7\x01,\x12t\x9d\xa0\xe0\x84\xfeY\x02\xedO\x80\x90\x84\x92$!\xc5$\xd8;\x01\xfd\x12L\x7fA\xa1\x92\x9c\x0c\'S\xec\xa1w\xfb\x89jjO3dO\t\xbf\'\xa8\xf7\xf0\xb4}\xac\x10\xb2O4\xf8\xf6\xa2\xebO"\x82<{\x94\xb6\xa7E\xb2\xdf\xaa\xc7\\\xd1\x1d\xdd\xa3\x93=\x9a\xda\x8b\xfe$\x87\xedE\x11R\xaf\xecU=f\x8f\xd2\xf6\xec~om\xf9\xeaR\xadqE=rE\xa3\xeb\x8a:\xe7\x8a:\xe7J\xea\x9c{\x11\xa9s\xae\xa8\x94\xae\x04\xc5\xafE$\xbf\\\xd1l\xbb\xa2_u\xc5\xe6\x8a\x12\xca\x82\xe7\xc5\x9a\xc6z\xb1\xae\xb8P$\xc0\x8b`H\xb1\xa8\x10Q\xf4\x15N\x8ad\xe5"\x80T\xa4<*\xb6\x15\xc7\x8a\x1c\xa0\x15#\x85\x93"\xed\x87\xe2D-[\x84P\x14c\x05\xd0"\xa7\x87\xc5\xad\x1a\xaeH\xfe)\x9e\xd4.(S\xb4\xb6\xac\xf64\xc5\x8cr\xb2"\x14\xa8\x88\xbb\x17\xf1\xe6\x8e\xaf\x88\xd4\xa1r\xefp\x9b\xa1C=\xd7\x81rt\xd0_\x87\xf6X\x87\xc2\xb7#\xbb\xff&"-\xafN\x131Q\x07\xed\xd01\xec\x80n\x1d\x1a\x82\x1d\x02\xaa\xa3\x8a0\x1d\xd0\xb6\xe3\xb02\xee\x85t\xb8\x17\xd2\xb1N\x1d;\xec~\xcb\x81\xdf/p\xeaZ\xbc2\'O\'\x1a\x1a\xbf\x12\xb5\xdc/Y\xb0T>\xbfR5\xd7\x1d\xfc\xe6\x8e\xe0\xba\xc3Dw\x04\xc9\x1d\xa5\xfc\x1dArG\xe8\xdc\x11$w9\x8d\x81;\t\x129\x0e\xbb\x93EJ\x82\xb9\xa3\x9dp\xf7E\xc3\xa1\xc5\xed\x8a;\xab\x81F\xeb\xbeb\xc5o\x05\x9dT@\xbd\n\xc0ZaG\x15vT\xc1\xa7*\n\xa1\xa6\x92\xf9(r2\x95g\xf4^\xe1\xeeH\xa5\xc9\xefH\xf7\x95\x10\xb1\xad\xc1S\xc1\xa9*O\xea>\x95\x8a\xee\xb9R\xd7\xf0\xabp\xdf\xa6\x12\xa8\x87V\xc4\x85\x7f\x88\xc8\x8d\x9dJ\x81\xc9\xf2\xea(\x15\xc8E\xa5\xc8\x80\x1f\xac\xa1\xc4S*\xe4\n9\xaaB\xa3\xb5B\xc2\xab\x08\xceK\xbb\xadB2\xaf\x88\xf7\x08\xa2WH\xe6\x15\x12Ae\xa4\xc8Q\xa1\xd7\x98\xa5\xb0\xce\xaeu\rY\x8a\xf0,\r\xd1,\xb6\xf7\xb0a\x16\x92\x90\x85\x82f9O\xce\x92\xad\xb2\x9c\xa8e\xa1$Y\xc8f\x96s\x80,\xa1\x9c\x85E\\\x8b\x01\xe4\xf8?\x0b\xad\xcc\x82\x0b\xd9H\x8d\x95m\xf26i;\n^g\xe9@e\xf1\x87lU\xed\x96-3\x96.h\x96r(+\xfe \x80\x9e\xad\xf1b\n\xaa,\x9d\xd8l\x81\x9fy\n\xb6\xd9\x92:W\x96\xcb\x1c\xd9"/\xf6\xd9\x85\xc4\xf71\xb1\x99\xe3!\xb3\xc6@jUT\x0b\xfbv\x13\xa7*\x9eL\xf8$\xa3\x89\xb4\x94PL1c\n\xb1I\xc9\xd1)Q\x99\xd2\x01H\x89\xeb\x94hO\xc9\xe7\xdf\xa8\xae\xbei\xae5\xdf\xa8\x98\xbeQ\xcb}\xb3\x96#\x9e"\x97`R|8\xc5SR\xf1\x1fa0)EP\xfa\x0b\x11\x0fL\xc7\x1a\x10)\xa7\x85)\xae\x9f\xd2\x92O!\xafi\x9f5\xd0\xbeOi\x87y\xa1z`\n7M\x0f\xea\xb8\xe9\x9e\xc9\xe0\xa6\xdf\xacb8%\x1b\xa7\xc4u\xca-\xa3\x14r\x9a\xc2\xc9R\x98Z\x83}6\xe8f6h&4\x92\x8f\xa7\xa6Erk\xf0\xe2\x06i\xb7\x81\xef7\xa08\r*\x9b\x06\xd7\x85\x1a\xa4\xf3\x06d\xa6Am\xd4\xa0\xbaj\xf8\xfc\xec\x07O\x9f\x11\xe1@\r\x9a\t\r\x88O\x03Do\xb4\x18@\x0f\xa2\x01\x8c7:\xec\xc2J\xd1\r\\\xbcA\xc9\xd4\xb0\xda\xb7\x0b\x92m\x03\x8e\xd3\x80\xb36,\x05\xe2\xee\x0bk\xe2\x93me\xff16\x88\x01\xdf\x18W\x8aa+1n\x17\xe3\xa2\xf1P\x8d\x14c\xe6x\xccX\\?\xc6\xf5c\xc2$&-\xc4\x80o\xbc\xd0\xe0\x89q\xaax\xc9\xdb\xc8<\xf1\x8a\xb1\xb0\x99\x18g\x8d9(\x8f\xa9\xbabJ\xb8\x983\xc0\x980\xb9\x82\xac,\x80\x8b\x05Zm\x9dTy#\xbf\x03|b(A\x0c:\xc5\x90\xf7\x98c\x9c\x18\xc3\xc4\xa0^\xcc;b\xe0+\xb6\x88\x8b\xebk`\xbb\x9c\xc0\xb9\x9c\xb5\xb9\x82\xda\x92O\\\xf1}I\x85.G\xb6n\x9e\xb1u\xc4\x1a?\xe3\xac\xcd%\xa6\\\xb2\x8c[\xe6gD\xa5\xfb\xc8+\xda\xea\x11.\'p.gm.w\x86\\\xce\xda\xdc&\xf3r\xd6\xe6\x86\xfa\xd4!\xc5\xba\x9c\xc09\xdc>q)\xf5]2\x8ck\r\xa0#\xe4\x12\x03.g\xba.\xa5\xbeK\xa9\xba\xd9\xf1\x94\xbb4.Wl\\b`\x83\x83\xba\xdc\xa3q9\xecp\xc5W\x85\x1a\xb9\x90\x95\r5\xb2\x8b\xaf\xba\xc4\x80\x0bww\xd7h\x12\xf6\xb5\xe1\xfe\xc2\x86\x1do\xe8vm8\xe1s9~\xdap\x14\xecr\xd8\xe1\xda\xa7K\x1b+s;\xd6\xd5f\x1a\xe0\xaev\xd33\x1bBf\x83;\xbbV\xf7\xd1u1.a\xe0f\x99\x98\x88\xd80`\xe3\xa2,x\xc0\x86H\xdb\x90\xd07\xf0\x80\r\x01\xea\xa0\xee\x11\x17\\G4\x17#\x16\x1c\xb1\x8d\x88P\x8ch]E\x16:G\xb24\xc92\x11\x0b\x8e\xe4\xcdB\x1a"\xbd\xc8o"\x80::\xe9\xb5$\xf2A\x8d\x13a\xf4\x88l\x1a\x01f\x11\x1d\xd7h\xc3\xd8\xa9*0\xa2=\x16QKF)K#\xcfG@r\x84\x0fF\x84D$\x81"\x146J\x18\x10)4DT\xb9Q\x07Q@@\xca\xeb\x88\xcb\xb7\x11\x17u#\x92{TV\x18\x89\xe8JF\xa0OTg\x00\xd9?\x82\xb7Fy\xe6\xf5\x18Ku3\xc4\x9eC\xac<\x14\xd3\xca\x9d\xcc!.3\xc4e\x86\xda\x1e3C<mH6\x1eb\xef!$q\x88\x07\x8f\xf0\x9e\xa1\x15GC\x02w\x08b\x0c\xe9h\r\xe9h\ri\xb6\x0fi\x97\x0ci\x9a\r\xb1\xcb\x10\xee8\x04\x94\x86\xdc\xe4\x1f\x02kC\xcd\xbbf\xc4\xe6\x1c\xa9\xb4\xa5\xfe>\xb0\xcf\x03\x9b;\xb0\xe5\x03\xfb<\xa0\xb4\x03\xaa<\xa0\xbf\x03\xaf8`\x81\x03v9\xa0\xa9\x11o\xbb\xa63p\xcd\xd5\xafk\xdag\x07K\xab\xd7\\\xfb\xbf&\x8b_\xd3r\xb8\xa6\xe5pM\x1b\xe1\x9a\x0e\xdc\xb5\xac]: \xd7\xec\xf3\xda\xda\'Z=PU\x1e\xe6\xfa\xb3\x03\x08y\xa0\xbds\xe0`\xe3@\xf7\xeb\x00\xf8\x1e\xc8<\x07\x0e+\x0e\xc0\xf7\x81\xabI\x07\xa0\xfe\xb0d\x06\xfc\xe8@\xff\xec\x00\xe8\x1d(\x93}\x0bz|\xd0\xcbg\xcb\xbe\x85o\xbe\xc2\x9e\xf1\x81/\x1f\x8b\xfb\xdc\x88\xf7Aa\x1f\x83\xfaX\xdc\xa7\x7f\xe1\x13\xcb~\xa0p\xe1K\xdcK\xe9\xea\x83\x11~Y\xd1\xc0\x87u\xf8\x12\xe1/"B\xea}>_\xf2\xa9b}j\x01\xbf\xc0\x0cy\x96\x0e\xd5\xf7\xa5\x00\x10\x92\xed\xbf\xf0bN{\xfc\x0e?\x83\xdf\xfb\x94\xf0>=\x1f\x9f\n\xc1\xa7\xe7\xe3\xd3"\xf1q\x19\x9f\xfbZ>\xc7L>W\xe3|\xf1\x08a\xbd\xbex\x84d.\x9fF\x84Oq\xe8\xe3S\xfe\x9e\xb7Au}\x9af>\xd0\xe3C@|r\x91\xbfd\x91\xe2i\xbfE\xa47\xf3|\xf2)1\xe73\x01\xf3\x8co<\x8b9\x9fE\xa4_\xf5La\xf6\x0c\xbd}~V\x13\xfd#\x88$\x14\xfa\x1f.\xc5?\x8b1\xa4)\xf1\x0c\xb3\x99Zh0\xe5lc\x8a\xafN9?\x9d\x02ISh\xfa\x94\xb5O\xc1\xa1)\xa11\xc5\x99\xa7\xc0\xd7\x14o\xbfg\x86{\x1a\xf6\xf7\xf4Y\xef\xef\xf4m\xf79]\xef=Pw\x0fN\xdd\x83^\xf7|\xe0t\x0f\xd2\xdd\x0bzIk\xf4\x1eL\x9bb\xfb)\x1f\xd5Ma\x86\xd3\xa1b\xc4\x14\xc0\x99\x02oS\xe0mJG\x7f\n\xeb\x9d\x92J\xa6P\x87)04\xe5\xb6\xea\x14\xef\x99\xc2d\xa6$\xb9)e\xd9c\xa0\x0e\xf1\xe8+L=J\xf8J[\xf3\x99\xf3\xd5GV\xf6(K\x17\xa2\xf2\x88C<ri\xf4\x11k>b\xa1,*1\x0c\xf8\xafM\x80?c\xf0\xcf\x18\xfc3\xa3?\xe3\x1c\x9f/x\xca\x8d\xa1\xcf\xa0\xe2\x92\x88Y\xa2\xaa%Lo\x89~\x96\x1bDBu\x89\xaa\x96\\D^\xd2\x96\xfcl/~I\xd5\xb4D-K\xd8\xe2\x12;/\xb1\xfe\x92\x84\xb5D\xc7K>\xbf\\b\xfd\x1b\xf2\xe7\xd2\x8a\xbf%j[\x12\x1cK\xd8\xc1\x92\xfe\xc5\x92P\\\xc2:\x96\x98i\x89\x8a\x97(\xfe\x86\xa7\x01c\x03W!\'\xb0\x06h\x88\x9b\x80,\x16\x80\x0c\x01\x9d\x95\xe0\xb4\r\xf1\xb6\x806_@\x9a\x0fh\xf3\x05c\x8d\xe6\x00\xfa\x15\xd0Y\t\xf8\x10"\xe0\x849\x80\xd6\x05 n@\xfb+ u\x07DR@\xc6\x0f$P\xaa"rn\x15\xd4\x11\xb9\x04\x10Ty\xca\xf5\xc5\xa0\xac0\x1cH\xd2\x14\n\x1d\x94\x18\xcb\xd7\xb2\x01\x07\x04A\x01M\xf1\xe1l\xe0\xf1TR\xa9\xa4\x82\xa0\xc3+\xc8\x94\x01\xb7\xc1\x03:\xdc\x01UE\x10\xaaO\x05Z`\x98\x1en\xd2\xe3\x10\xbb\x87\r{\xd8\xbb\x87\x9b\xf4\xf0\x8d\x1e\xde\xd5\x83\xfd\xf7\xbe2\x16\xaf\xed\xbd\x02v\xbd\x81Z\xa0\x07\\\xf6F\x0c\x80\x8f\xf7z\x0c\x00\x18{TZ=\x82\xab\x97j\x18\xf5\xc6LF \xf6h\x9f\xf56\n\x97=\xdc\xa4\xf7\xc6\xcap\xa9\x1e\x05F\x8f\xa6m\x0f\xe8\xb8\xb0Ab{\xfaC\xc0\xd3\xa13ra5)\xb7\x84\xf0\x05J\xbe@\xc9[\x14wA$]X7E/2\x1c\rl\xad\x1f2\xdd\x96\x8b}[\x8e\xd5\xb6\xd8w\x0b\xa6n\x7f\xf2\xbe\xba:\xcbE\x11\xd1G,!\xfe\x97=]p\'\xec\xa2\xa3\xe2\x16%m\x856\t\xff\xd9\nmz\x17\x91\x8b\x9c[\xda\x8d[\x94\xbf\xc5$\x17\t\xf3\x02\xf7[\x92\xc0\x16\x1e\xb8\x05S\xb6|c\xbe\xa5\'\xba\xe5\x90xK\x83uK\xf9\xb7\xa5\xed\xb5\xe5\xde\xfeVPI\x9aV\xdbX]hK\xf1\xb1\xed)\xae\xb5\x0e\xba\x9c\x16m/\xcf\xeaA\xb6V\xaa\x93{\x0b\xed[\xb4\x17Zd\x94\x16I\xb9ES\xb9\x05]\xf5\x08\xe3\x960\xedc\xef\xdbx\x1c\xc3\xb4\xba\x8a\t-\xb1\x91\x90\xf9\x96\x80\x86\xd4\x0b-\x81\x12\xa9\x17<q*\xb9l\xdd\x82t{\xe2T\xc2*[\xfc\xb3\x82\x16\xa7\x04-N\xc8Z\x94\x19\xad\no\xa3\xa0hq\x87\xbf\x05qm\t\xf4\xc9)\x96WPP\xf6\xf2\xac\xc1\xfa\x19q\xe2q\x19\xc3\x13\x0f\x15\xa6\xe3Uto\x1e\xb7\r<\xaa\x1e\x0f\x84\xf7X\xba\xc7\xb1c\xcb*\xde\xbc\xa6\xc6\xa2\x17\xb1`\xce\x19<\xa0\xd8\xa3\xc0\xf1:<}\xd2\xdd{\x94H\xde3O_P\x8f\xa3\x9e\xdf"j\xbd\xbeb\xa3\x07/\xf5\x06\n}\xde\x08\x91\xa3\x05\x0f\x14\xf4\xe8cyP\x97\x16\xf7\xe8<\xd0\xd5\xe3h\xc1#v<J\x19\x8f\xa3c\x8f\x98\xf4V,\x92\xf3\x04\x8f\x00\xf7 f\x1e\x9f\xe3y\xf4R=>\xfc\x1c1\xd6\xa1\x976\x82\xef\x8e\xacf$k\x18\x81\x0b\x0e\xa1\xec\xf0\xbd\xbeC#\xd9\xa1\xbd\xecp\x99\xd2Ag\x0e\xd9\xcb\xa1m=\x02\xdd\x1c(\xdc\x88\xb3\x9d\xd1P\xb53"\xd3\x8d\xe8D8\xb0\x15\x87\x96\xc2\x88;\x98\x0e-n\xc7R\t\xc7\xed#\x8c\xe5\xf0\xa5\xd1\x88\xa5\x8f\xc6\xea\x04\x0e\x07\xd5\x0e\x9f\x0c9\x1cn8|t\xe4p\x10\xe2p<\xe2\xf0\xb9\xaf\xc3\xd7\xc1\x0e\xdf\t9|S\xe4p\xce\xe1\xf0\xfd\x91\xc3\x99\x88\xc3\xb7J\x0e\xe7\'\x0e\xdf\t9\x9c]8|S\xe4p\xce\xe1p\xfa\xe1p&\xe2pR\xe2\xf0\xad\x92\xf3\xc2+\x9e\x99\x8c\xd3\x8f\x11\xe1\xe4H>\x94v\x80c\x14+\x1c>\xffv\xfe\xf5!\x1a\'ct\xb2\x7f\x8eO\xa5\xdf\xe7\xc8\x89\xb7\x90=\'\x8b\xc8\xb5\xbf\x11\xd5\x8fC\xfev\xa4B\x95km\x0eu\xab\xc3\xb7\xec\x8e\x94\xbbR\x04\x8f(\x84\x1c)w\x856;R\x04Ki<\x82\xaa9R\xcd~\x11\x91\nc\x04\x81\x1bY\xe9\xe7\x1d\xa2\xf5N\xbd\xf2N&z\xc7\xbb\xde\xb9d\xf8\x0e\x1f\x7f\x87\xa5\xbf\x13#\xef\xef\x1a\xb2\xef\x94`74\x9b\x1cB\xf6f\xa0;z\x87\xd3\xbc\xbb\xbc\xcd\xda\xdcZ\r\xf7\x0ef\xbe\x83\x99m\x0e|\x1c\xf0\xea\x86\n\xff\x06]\xdf\xd0#\xb8\xa1\xefyC\x8f\xe0\x86/\xacnh\x9d\xde\xd0P\xbd\xa1\xf7pC+\xe4\x86\xf5>nu\x17\x0eHZ\x12\xbf\x17\xe4/\xd1\xe5/\xd1\xfb/q\x03\xa9D7\xbeTR\xff,q\xd7\xa8D]R\xa23X\xe2\xba\x7f\tU\x97\xb0E\x89{\x0f%\x0c[\xe2\xf3\x84\x12Ek\x89\xa3\xe6\x92u ^\x82\xaf\x96\xc4\x02R\x14\x948\xed)\xb9\xcc\xc6\x8d\xbb.\xed\xc9.]\xcd\xae,X\x9a\x80]z\x16]v\xdf\xa5\x90\xea\xc2R\xba\xa2\xbfS\xce\xee\xd28\xee\xe2\xa0].\x83t\xed\xcfA\xce!K)\xd0|N\xa4u\t\x99\xae\xab\xf6\xe8\xe2\xa2]\x8b/t\xf5\x03a\xd3\xa5L\xeeBZ\xba\x14\x02c\x9e\xce\xa8|g\xe4\x92\x19\xb7\x07f\xe4\x92\x19]\x8bY_w:\xa3\xee\x98Q\x1f\xcd\xb8:2\x9b1\xc3\\\x83c\xcd\xe6f\x84\xf8\x0cE\xccH\xc53\x92\xf9\x0c\x7f\x9e\xe1V3R\xf1\x8c+\xd93:\xa63\x90\xe1\x9c/\xd8g\x00\x91\x99Q\xa2\xce0\xc1\x8c\xae\xc7\x8c\x18\x9f\x11_3\xac1\x03Zg\xd6\xe6P\xfb\x0c\x18\x9ea\x81\x07&{`\xb2\x07y\xb1$\x93\x87\x07\x9erq\xf2\xe1Zq\xfa\xe1F\x01\xf7\x81\xcd=\\\xf1\x14\xecx\x00Q\x1e\x04;$\x83<\x08\xa2H/\xb2\xea|\xc4\xb8\xa9\xe2GUb\xaaj9]\x95\x05W\xd9Q\xf5\xa4V\x89\xaaj\xacJ\xa9R\xefT\xb1x\x15\x86X%\xca\xab\x90\x8e*uK\xd5\xd7x\xaf\x12\xc3\xd5\x9a\x06n\x95\xb8\xac\x86\x8aUU\xae\xe5U\xb9\xb1Y\x85\x13\x9f\x91\xc4\xcf:\xfa\xe2\xb3\xa6\xae\xec\x0c\x1ap\x161\x00\xd2q\xc6\xbf$;\xcb\xeb\x80\xefv\xad~\x86{\x9cQ\r\x9f\xd9C.\xf1\x95\xdfh\xb6\x85\xf8\x9b\xff\xfe\xd2\xa4Q\xd0\xdc \xc2T\x9b\x07u\xdd&`\xd4\x14#\xc8\x19@\x13\xf6\xd9\x9c\xa8\xb75Sf\x00\x80\x9b\xdc\x82lF\xaa\xcd\xa6hH0\xbe\xd9A$\xa34\xf9\xf8\xb6\xd9U\xfcmr\xa2\xd3\xa4\xbejr7\xb2)\x8a\x95z\xb0I\x1ai\xd2\x15kr\x81\xac\xe9\xf06"\xa9\x89\xce\x9a\x94LM\xeb\xf8\xac\xcf\xc7\xab\xfd\x89j\xb5\xcfU\xa8>t\xa4\x0fI\xe9S\x15\xf4\xa9\xc9\xfb\x16HR\xe6\xf4\xb9\x98\xd1\x07\x7f\xfa`U\x1f\x04\xeb\x93\x9c\xfb\xd8\xb0\xbfa26\xd7\'\xab\xf5\xd9g\x1f|\xeaS\x9c\xf7\t\xcb>\xf0\xd3\xc7\xd1\xfaV\x8b\xe0\x8d\x1d\xbd\xd1s~#X\xdf\xf8\x94\xfc\x8d\xb5\xbf\xb1\xe07\xdd\xa7y\xcb\x18\xfd\x19k\xcfc\xf0<\xdfB\xe5\xa9\xb8\xf3T\xc6\xf9@a$O\xb8\xe7\xdb\xcc\x00\x8d\xc9\x13\xf9y\x02;O\xea\xcd\xd3\xe7\xcb\xe3\xd7y6\x94\xe7\x7ft\xe5\xe9\xd2\xe5\xe9\xe0\xe6\xb1\xe1F\x9b&&\x0fH\xe692\xcbc\x97\xbc\x85\x97yL\xd0fD\x1b\xf5\xb4\x15}3#,\xd7\xde\xe8z\\\x98q\x9b\xfbDm\xc9\xab\xc2\xfd\xda3\x1d\xdb\x06D7\xd6\xcf\xba\n\xa2m)S\xe4\x18\xb6M7\xb7\xcd1M\x9bo\xdf\xda(\xb8\r\x18\xb4\xeb\x1a\xa9m1\x9c\xb0\xc7\xb6\x18NZ\x1am\xba\x1bmxb\x9b\xeb\x9b\xed\xa2\x86r\xfb\x87"@\xdbS#\xb7i\xcc\xb4\xf3\x1a\xcac4\xf9\x89\x1c\xfd\xc9\xba\xaf4\xe6\x9e\xd3\'\x98\xd6\'2\xf3\'\xeb\xbf6|\x02\x9c\xc7\xf0\xe81\x86\x19c\xae\xb15\x96W\x8f9\x14\x19C%>\xd9\xf0>\xb6\x0fY\x80\xe41~5\x06\xd4\xc7\xc0\xc4\x98\x92b\x0cL\x8c\xe1Gc\xf8\xd1\x98o#\xc7\xf4\xa5\xc7\xb0\xea1\x1cm\x0c]\x1ds\x9bjLwaL\x95:\x86\xad\x8f\xb9\xc60\x16\xca(g\xdd\xe3\x01\x1b\x02\r7P\xc6[J\xa0[\xa11\xc2<n\xa1&\xb7P\x93[\xbe\xbc\xbd\xcd\xa99n\xf9\xc7\x11\xb7\x14Q\xb7\xfc\x93\x89[\x8a\xa8[Lw\xcbY\xee\x85e\xf2[<~\x04t\x8e\xfeZ\xf4\xff\xfe\x1f\xfa\xddI\x97'))
global t
t=' '
def f(k):
 global t
 r=a[t+k]if t+k in a else'e';t=k
 return r

1
Explication rapide: Le dictionnaire zlib.decompress('...')évalue {'G?':' ', 'G;':' ','G"':' ',.......}et acorrespond à un dictionnaire qui mappe de 2 à 1 caractère. Variante à deux caractères de la réponse de Steadybox .
user202729

1
Comme je peux le voir, le littéral est de 17780 octets. Vous pouvez le réduire à 11619 caractères en supprimant les espaces dans le contenu décompressé, ce qui enregistre 12 322 octets. (Si j'ai correctement compté) De plus, la conversion de codes d'échappement hexadécimaux en caractères bruts réels peut économiser encore plus d'octets.
user202729

Comment puis-je poster quelque chose ici s'il s'agit d'octets bruts?
Skyler

1
xxd, hexdump, uuencodeOu similaire
Peter Taylor

@ user202729 Notez simplement que le code Python ne peut pas contenir d'octets NUL bruts réels.
mbomb007

4

Haskell, (1904 + 1621 + 208548 + 25646) * 2 + 371705 = 847143

{-# LANGUAGE FlexibleInstances, DeriveGeneric #-}

import Control.Arrow
import Control.Monad
import Control.Monad.Trans.State
import Data.List

import System.IO
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Char8 as BC8
import Data.Ord
import Data.Char
import Data.Monoid
import Data.Maybe (fromJust, catMaybes)
import Data.Function
import qualified Data.Map as Map

import Codec.Compression.Lzma

import Data.Flat

import GHC.Word

maxWordLen :: Integral n => n
maxWordLen = 20

wordSeqDictSize :: Integral n => n
wordSeqDictSize = 255

predict :: [Trie] -> Char -> State ([Either Char Int], String) Char
predict statDict c = do
   (nextChar:future, begunWord) <- get
   case nextChar of
     Left p -> do
       put (future, [])
       return p
     Right lw -> do
       let wpre = begunWord++[c]
       put (future, wpre)
       return $ trieLook (tail wpre) (case drop lw statDict of{(t:_)->t;_->Trie[]})

newtype Trie = Trie [(Char,Trie)] deriving (Show, Generic)
instance Flat Trie

trieLook :: String -> Trie -> Char
trieLook [] (Trie ((p,_):_)) = p
trieLook (c:cs) (Trie m)
 | Just t' <- lookup c m  = trieLook cs t'
trieLook _ _ = ' '

moby :: IO (String -> String)
moby = do
    approxWSeq <- BSL.unpack . decompress <$> BSL.readFile "wordsseq"
    Right fallbackTries <- unflat <$> BS.readFile "dicttries"
    seqWords <- read <$> readFile "seqwords"
    let rdict = Map.fromList $ zip [maxWordLen..wordSeqDictSize] seqWords
    return $ \orig ->
      let reconstructed = approxWSeq >>= \i
             -> if i<maxWordLen then let l = fromIntegral i+1
                                     in replicate l $ Right l
                                else Left <$> rdict Map.! i
      in (`evalState`(reconstructed, ""))
              $ mapM (predict fallbackTries) (' ':orig)

Exemple:

Call me Ishmael. Some years ago--never mind how long precisely--having
 ap  me ,nhmael.  Hme ?ears |ce--never  usd how long .aacesely--|ubing
little or no money in my purse, and nothing particular to interest me on
little or no ?ivey in my ?efse, and ,uwhing .hrticular to Bdaenest me on
shore, I thought I would sail about a little and see the watery part of
?neae, I thought I would  cfl about a little and see the |rkers part of
the world. It is a way I have of driving off the spleen and regulating
the world. It is a way I have of ,uiving off the |kli   and .ia       
the circulation. Whenever I find myself growing grim about the mouth;
the Ca         . B        I  rtd |yself ,haoing  eom about the ?ivlh;
whenever it is a damp, drizzly November in my soul; whenever I find
Baieever it is a  'mp, ,uiv    Bar      in my  cfl; Baieever I  rtd

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.
  • dicttriescontient, 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 wordsseqfichier est encore trop gros pour être compétitif.

Voici une version complète qui crée les fichiers et effectue l'analyse:

depunct :: String -> [String]
depunct (p:l) = (p:take lm1 wordr) : depunct (drop lm1 wordr ++ srcr)
 where lm1 = maxWordLen-1
       (wordr, srcr) = (`span`l) $ if isAlpha p
                 then \c -> isLetter c || c=='\''
                 else not . isAlpha
depunct []=[]

mhead :: Monoid a => [a] -> a
mhead (h:_) = h
mhead [] = mempty

limit :: [Int] -> [Int]
limit = go 0
 where go z (n:l) | z<100 = n : go (z+n) l
       go _ l = take 1 l

packStr :: String -> Integer
packStr = go 0
 where go n [] = n
       go n (c:cs)
        | c>='a' && c<='z'  = go (28*n + fromIntegral
                                   (1 + fromEnum c - fromEnum 'a')) cs
        | otherwise         = go (28*n) cs


mkTrie :: [String] -> Trie
mkTrie [] = Trie []
mkTrie strs = Trie [ (c, mkTrie . filter (not . null) $ tail<$>l)
                   | l@((c:_):_) <- sortBy (comparing length)
                                  . groupBy ((==)`on`head)
                                  $ sortBy (comparing head) strs ]

mkTries :: [String] -> [Trie]
mkTries rsrc = [ mkTrie $ filter ((==l) . length) rsrc
               | l <- [0..maximum (length<$>rsrc)] ]

main :: IO ()
main = do
    orig <- readFile "whale.txt"
    let wordchopped = depunct orig
        dictRes
          = take 5000
          . map mhead
          . sortBy (comparing $ negate . length)
          . group . sort
          $ wordchopped
        dict = Map.fromList $ zip dictRes [maxWordLen..wordSeqDictSize]
        rdict = Map.fromList $ zip [maxWordLen..wordSeqDictSize] dictRes
        approxWSeq = [ case Map.lookup w dict of
                        Just i -> i
                        Nothing -> fromIntegral (length w - 1) :: Word8
                     | w <- wordchopped ]
        fallbackTries = mkTries . drop (wordSeqDictSize-maxWordLen) $ dictRes
        reconstructed = approxWSeq >>= \i
             -> if i<maxWordLen then let l = fromIntegral i+1
                                     in replicate l $ Right l
                                else Left <$> rdict Map.! i
        predicted = (`evalState`(reconstructed, ""))
              $ mapM (predict fallbackTries) (' ':orig)
        incorrects = length . filter id $ zipWith (/=) orig predicted
    putStrLn $ "longest word: "++show(maximum $ length<$>wordchopped)
    putStrLn $ show incorrects++" errors / "++show (length orig)++" chars"
    BSL.writeFile "wordsseq" . compress $ BSL.pack approxWSeq
    BS.writeFile "dicttries" $ flat fallbackTries
    writeFile "seqwords" . show $ take (256-maxWordLen) dictRes
    writeFile "whale-approx.txt" . unlines $ coLines orig predicted

coLines :: String -> String -> [String]
coLines [] _ = [[],[]]
coLines ('\n':l) (_:m) = []:[]:coLines l m
coLines l ('\n':m) = coLines l ('|':m)
coLines (c:l) (d:m) = case coLines l m of
   (lt:mt:r) -> (c:lt):(d:mt):r

3

C ++ (WIP), 1923 * 2 + 1017344 = 1021190

#include <map>
#include <random>
#include <string>
#include <type_traits>
#include <vector>

using namespace std;

constexpr minstd_rand::result_type seed = 10087702;

template<typename T>
class discrete_mapped_distribution {
private:
    discrete_distribution<size_t> distr;
    vector<T> values;

public:
    discrete_mapped_distribution() :
            distr(), values() {
    }
    template<typename I, typename = typename enable_if<is_arithmetic<I>::value,
            I>::type>
    discrete_mapped_distribution(map<T, I> distribution) :
            values() {
        vector<I> counts;

        values.reserve(distribution.size());
        counts.reserve(distribution.size());

        for (typename map<T, I>::const_reference count : distribution) {
            values.push_back(count.first);
            counts.push_back(count.second);
        }

        distr = discrete_distribution<size_t>(counts.cbegin(), counts.cend());
    }

    discrete_mapped_distribution(const discrete_mapped_distribution&) = default;
    discrete_mapped_distribution& operator=(const discrete_mapped_distribution&) = default;

    template<typename URNG>
    T operator()(URNG& urng) {
        return values.at(distr(urng));
    }
};

class generator2 {
private:
    static map<char, discrete_mapped_distribution<char>> letters;

    minstd_rand rng;

public:
    static void initDistribution(const string& text) {
        map<char, map<char, uint64_t>> letterDistribution;

        string::const_iterator it = text.cbegin();
        char oldLetter = *it++;

        for (; it != text.cend();) {
            ++(letterDistribution[oldLetter][*it]);
            oldLetter = *it++;
        }

        generator2::letters = map<char, discrete_mapped_distribution<char>>();

        for (map<char, map<char, uint64_t>>::const_reference letter : letterDistribution) {
            generator2::letters[letter.first] = discrete_mapped_distribution<char>(letter.second);
        }
    }

    generator2() :
            rng(seed) {
    }

    char getNextChar(char in) {
        return letters.at(in)(rng);
    }
};

map<char, discrete_mapped_distribution<char>> generator2::letters;

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

  • 2018/01/24 : réponse initiale publiée.
    Graines contrôlées: 0-50000. Note: 2305 * 2 + 1017754 = 1022364
  • 2018/01/24 : a fait du golf minimal. Ajout d'un lien vers la méthode de calcul du score.
    Graines contrôlées: 0-80000. Note: 1920 * 2 + 1017754 = 1021594 (-770)
  • 2018/02/02 : Nouvelle graine (10087702) (n'a pas trouvé le temps de corriger la soumission)
    Graines vérifiées: 0-32000000. Note: 1923 * 2 + 1017344 = 1021190 (-404)

Pourriez-vous inclure un harnais de test dans votre réponse pour évaluer le score?
Nathaniel

@Nathaniel J'ai lié le code de partition directement. De plus, avec le référentiel, le considérieriez-vous suffisamment?
BrainStone

Après avoir examiné les règles, j'ai remarqué que j'en violais certaines. Je
mettrai

Vous finirez alors par encoder le texte dans la graine aléatoire. Voir le langage de programmation ésotérique Seed , et vous voudrez peut-être procéder au reverse engineering du programme MT19937 et battre cette réponse (si vous le pouvez).
user202729

Bonne idée, mais ça ne va pas aider à faire un bon score. +1 quand même.
user202729

3

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.

x="\"ect,htabsdd,in,\\nodniwlrfydbulkm;f?ckgwvi0,.*pr;\\\"uz17klI\\n-c'WSpA\\nTwqu8.77!-BeWO5.4.CoP\\n\\\"UHEFu2.?-9.jo6.NI3.MaLYDOGoOAR'QUECziJoxp(\\nYa:\\nVI);K\\nUS*IZEX\\n&\\n$\\n_y[S\""
f=->n{(x.include? n)? x[x.index(n)+1] : ' '}

Comment j'ai généré x

Tout d'abord, j'ai généré a.txtavec ce qui suit:

grep -o ".." whale2.txt | sort | uniq -c|sort -bn>a.txt

Puis j'ai généré a.csv:

cat a.txt | awk '{ print $1","$2 }'|sort -n|tac>a.csv

Ensuite, je l'ai analysé xavec le script Ruby suivant:

f={}
File.open('./a.csv').each{|l|x=l.partition(',')
f[x.last[0..1]]=x.first}
n={}
r={}
f.each{|k,v|if((r.include? k[0]and v>n[k[0]])or not r.include? k[0])and not k[1].nil?
r[k[0]]=k[1]
n[k[0]]=v
end}
s=''
r.each{|k,v|s+=k+v}
puts s.inspect

Comment j'ai marqué

w=File.read('whale2.txt')
x="ect,htabsdd,in,\nodniwlrfydbulkm;f?ckgwvi0,.*pr;\"uz17klI\n-c'WSpA\nTwqu8.77!-BeWO5.4.CoP\n\"UHEFu2.?-9.jo6.NI3.MaLYDOGoOAR'QUECziJoxp(\nYa:\nVI);K\nUS*IZEX\n&\n$\n_y[S"
f=->n{(x.include? n)? x[x.index(n)+1] : ' '}

score = 235
w.each_line{|l|v=l[0];l[0..-3].each_char{|n|v+=f[n]};v.split(//).each_with_index{|c,i|if l[i]==c
print c
else
print '_'
score+=1

end}}

puts "FINAL SCORE: #{score}"

Je suis sûr que c'est permis. si vous avez analysé le fichier, bon travail! Ce n'est que si le programme le fait est invalide.
Erik l'Outgolfer

@EriktheOutgolfer> _> (glisse silencieusement un "(non-concurrent)" dans le titre)
NO_BOOT_DEVICE

Pourquoi? Si cela est valable, c'est la compétition, même si elle ne bat pas beaucoup. Si elle n'est pas valide (c'est-à-dire que votre solution lit dans le fichier et ne contient pas uniquement un littéral), elle doit être supprimée.
Erik the Outgolfer

Hmmm. Je pensais que vous vouliez dire si un programme analysait le fichier, et pas seulement la solution.
NO_BOOT_DEVICE

1
Je ne peux pas lire Ruby, mais je pense que cela est valable. Avoir le littéral dans le programme est tout à fait correct, ce n'est pas un problème du tout.
Nathaniel

2

Python 3 , (146 * 2 + 879757) 880049 octets

def f(c):return"\n                     t \n 2  sS \n  -  08........       huaoRooe oioaohue thpih eEA \n   neo    enueee neue hteht e"[ord(c)-10]

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


Pouvez-vous économiser des octets en utilisant def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]?
Neil

0

[Python 3] (644449 * 2 + 0) 1288898 points

Une précision parfaite dans seulement 644449 octets

import zlib,base64 as s
t=enumerate(zlib.decompress(s.b64decode(b'###')).decode());a=lambda c:next(t)[1]

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.

import zlib, base64
with open("modified.py","w") as writer:
    writer.write("import zlib,base64 as s\nt=enumerate(zlib.decompress(s.b64decode(")
    with open("cheatsheet.txt","rb") as source:
        text = source.read()
        writer.write(str(base64.b64encode(zlib.compress(text,9))))
    writer.write(')).decode());a=lambda c:next(t)[1]')

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".

with open("out.txt","w") as writer:
    with open("whale2.txt","r") as reader:
        text = reader.read()
        for b in text:
            c = a(b)
            writer.write(c)

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.


il pourrait y avoir un "\ r \ n" là-dedans que je ne pouvais pas me débarrasser de Windows quand je les
comptais

2
ouais c'était une faute de frappe qui s'est propagée
Legorhin le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.