Scrivi a Moby Dick, approssimativamente


297

Ecco un file di testo ASCII da 1.2 Mb contenente il testo di Moby Dick di Herman Melville ; o, La balena . Il tuo compito è quello di scrivere un programma o una funzione (o classe, ecc. - vedi sotto) a cui verrà assegnato questo file un carattere alla volta e ad ogni passaggio devi indovinare il carattere successivo.

Questa è . Il tuo punteggio sarà

2*L + E

dove Lè la dimensione del tuo invio in byte ed Eè il numero di caratteri che indovina in modo errato. Vince il punteggio più basso.

Ulteriori dettagli

L'invio sarà un programma o una funzione (ecc.) Che verrà chiamato o invocato o inviato i dati più volte. (1215235 volte per essere esatti.) Quando è richiesto il n ° volta sarà data la n ° carattere whale.txto whale2.txte deve emettere la supposizione per la ( n + 1 ) esimo carattere. Il Ecomponente del suo punteggio sarà il numero totale di caratteri che indovina in modo errato.

La maggior parte degli invii dovrà memorizzare uno stato tra le invocazioni, in modo che possano tenere traccia di quante volte sono stati chiamati e quali sono stati gli input precedenti. Puoi farlo scrivendo su un file esterno, usando statico variabili globali, inviando una classe anziché una funzione, usando una monade di stato o qualsiasi altra cosa funzioni nella tua lingua. L'invio deve includere qualsiasi codice necessario per inizializzare il suo stato prima della prima chiamata.

Il tuo programma dovrebbe essere eseguito in modo deterministico, in modo che faccia sempre le stesse ipotesi dato lo stesso input (e quindi ottiene sempre lo stesso punteggio).

La tua risposta deve includere non solo la tua richiesta, ma anche il codice che hai usato per calcolare la Eparte del suo punteggio. Non è necessario che questo sia scritto nella stessa lingua dell'invio e non verrà conteggiato per il conteggio dei byte. Sei incoraggiato a renderlo leggibile.

Per quanto riguarda l'interfaccia tra l'invio e questo programma di calcolo del punteggio, tutto va bene, purché il programma fornisca sempre un byte di output prima di ricevere il successivo byte di input. (Quindi, ad esempio, non puoi semplicemente passargli una stringa contenente tutto l'input e riavere una stringa contenente tutto l'output.)

È necessario eseguire effettivamente il programma di test e calcolare / verificare il punteggio prima di inviare la voce. Se il tuo invio viene eseguito troppo lentamente perché tu possa verificarne il punteggio, non è qualificato per competere, anche se sai quale sarebbe il suo punteggio in linea di principio.

La Lcomponente del tuo punteggio verrà calcolata secondo le normali regole per le sfide del code golf. Se il tuo invio conterrà più file, prendi nota delle regole sul punteggio e sulla struttura delle directory in quel caso. Tutti i dati utilizzati dal codice devono essere inclusi nel Lpunteggio.

È possibile importare librerie esistenti ma non caricare altri file esterni e il codice potrebbe non accedere a whale.txtowhale2.txtfile in altro modo rispetto a quanto sopra descritto. Non è possibile caricare reti neurali pre-addestrate o altre fonti di dati statistici. (Va bene usare le reti neurali, ma devi includere i dati di peso nella tua presentazione e contarli per il conteggio dei byte.) Se per qualche ragione la tua lingua o le tue librerie includono una funzionalità che fornisce parte o tutto il testo di Moby Dick , non è possibile utilizzare tale funzione. A parte ciò, puoi utilizzare qualsiasi altra funzione integrata o di libreria che ti piace, comprese quelle relative all'elaborazione, alla previsione o alla compressione del testo, purché facciano parte della tua lingua o delle sue librerie standard. Per routine più esotiche e specializzate che includono fonti di dati statistici, dovresti implementarle tu stesso e includerle nel conteggio dei byte.

È probabile che alcuni invii includano componenti che sono essi stessi generati dal codice. In tal caso, includere nella risposta il codice utilizzato per produrli e spiegare come funziona . (Fintanto che questo codice non è necessario per eseguire l'invio, non sarà incluso nel conteggio dei byte.)

Per motivi storici, esistono due versioni del file e puoi utilizzarle in una risposta. In whale2.txt(collegato sopra) il testo non è racchiuso, quindi le nuove righe appaiono solo alla fine dei paragrafi. Nell'originale whale.txtil testo è avvolto con una larghezza di 74 caratteri, quindi è necessario prevedere la fine di ogni riga e prevedere il testo. Questo rende la sfida più complicata, quindi whale2.txtè consigliata per nuove risposte. Entrambi i file hanno le stesse dimensioni, 1215236 byte.


Per riassumere, tutte le risposte dovrebbero includere le seguenti cose:

  • La tua stessa richiesta. (Il codice, oltre a tutti i file di dati che utilizza: possono essere collegamenti se di grandi dimensioni.)
  • Una spiegazione di come funziona il tuo codice. Spiegare il metodo I / O e come prevede il carattere successivo. La spiegazione del tuo algoritmo è importante e buone spiegazioni guadagneranno taglie da me.
  • Il codice che hai usato per valutare il tuo punteggio. (Se questo è identico a una risposta precedente, puoi semplicemente collegarti ad esso.)
  • Qualsiasi codice utilizzato per generare l'invio, insieme a una spiegazione di tale codice. Questo include il codice che hai usato per ottimizzare i parametri, generare file di dati, ecc. (Questo non conta per il conteggio dei byte ma dovrebbe essere incluso nella tua risposta.)

Classifica

bounties

Di tanto in tanto offro taglie per incoraggiare approcci diversi.

Il primo, 50 punti, è stato assegnato ad A. Rex per la risposta con il miglior punteggio al momento.

Il secondo, 100 punti, è stato assegnato anche ad A. Rex, per la stessa risposta, perché hanno aggiunto un'ottima spiegazione alla loro risposta esistente.

Il prossimo premio , 200 punti , verrà assegnato a entrambi

  • Una risposta competitiva che utilizza una nuova tecnica. (Questo si baserà sul mio giudizio soggettivo poiché è il mio rappresentante che va nella generosità, ma puoi fidarti di me per essere onesto. Nota che la tua risposta deve contenere spiegazioni sufficienti per capire come funziona!) Tale risposta ha bisogno non prendere il punteggio più alto, deve solo fare ragionevolmente bene rispetto alle risposte esistenti. Sono particolarmente interessato a vedere soluzioni basate su reti neurali ricorrenti, ma assegnerò la generosità a tutto ciò che sembra abbastanza diverso dai modelli di Markov che dominano i punteggi migliori attuali.

O:

  • Chiunque superi il punteggio più alto di A. Rex (attualmente 444444), usando qualsiasi metodo.

Una volta rivendicato il premio di 200 punti, molto probabilmente ne offrirò uno da 400, aggiornando i requisiti di conseguenza.


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Dennis,

9
xkcd.com/1960 sembra essere un riferimento a questa sfida!
A. Rex,

Ho pensato di comprimere questo ... ma è un po 'troppo lungo che il mio computer si è bloccato
scrollando le

Risposte:


135

/// , 2 * 1 + 1020874 = 1020876

 

Stampa uno spazio.


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Dennis,

Questo è un hacking di ricompense estremamente intelligente! Devi essere un AGI;)
Alex

97

Node.js, 2 * 224 + 524279 = 524727

Si prega di fare riferimento al registro delle modifiche alla fine di questo post per gli aggiornamenti dei punteggi.

Una funzione che accetta e restituisce un byte.

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

Consiste in un semplice modello PPM che osserva gli ultimi 8 caratteri per prevedere il successivo.

Confidiamo in un modello di lunghezza L quando lo abbiamo incontrato almeno T [L] volte, dove T è un array di soglie arbitrarie: [1,1,2,1,2,3,5,2] . Inoltre, ci fidiamo sempre di un modello il cui primo personaggio corrisponde [A-Z '"(].

Selezioniamo il modello più lungo attendibile e restituiamo la previsione con il punteggio più alto associato a questo modello al momento della chiamata.

Appunti

  • Questo ovviamente non è ottimizzato per la velocità, ma funziona in circa 15 secondi sul mio laptop.

  • Se ci fosse permesso di ripetere il processo più volte di seguito senza ripristinare il modello, il numero di errori converrebbe a ~ 268000 dopo 5 iterazioni.

  • L'attuale tasso di successo della funzione di previsione è ~ 56,8%. Come notato da @immibis nei commenti, se le ipotesi sbagliate e corrette vengono mescolate insieme, il risultato non è nemmeno a malapena leggibile.

    Ad esempio, questo frammento vicino alla fine del libro:

    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.
    

    diventa:

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

    Sostituendo le ipotesi errate con caratteri di sottolineatura, abbiamo un'idea migliore di ciò che la funzione ha funzionato:

    _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'esempio sopra è stato creato con una versione precedente del codice, lavorando sulla prima versione del file di input.

Codice di prova

/**
  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)

Registro delle modifiche

  • 524727 - risparmiati 19644 punti passando a whale2.txt (aggiornamento sfida)
  • 544371 - risparmiati 327 punti forzando gli schemi che iniziano con una lettera maiuscola, una virgoletta, una virgoletta doppia o una parentesi aperta per essere sempre affidabili
  • 544698 - risparmiato 2119 punti forzando i motivi che iniziano con uno spazio di cui fidarsi sempre
  • 546817 - salvato 47 punti regolando le soglie e giocando a golf la funzione di predizione
  • 546864 - risparmiato 1496 punti estendendo la lunghezza massima del motivo a 8 caratteri
  • 548360 - risparmiato 6239 punti introducendo la nozione di modelli affidabili, con soglie che dipendono dalla loro lunghezza
  • 554599 - risparmiato 1030 punti migliorando la previsione dell'alimentazione di linea
  • 555629 - Hai salvato 22 punti giocando a golf la funzione di previsione
  • 555651 - risparmiato 40 punti giocando a golf la funzione di previsione
  • 555691 - punteggio iniziale

44
Per i curiosi, no, questo non produce niente come Moby Dick. È tutto 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. Riesce a ottenere qualche parola completa a volte. Come whales.
immibis,

23
@immibis Il titolo della sfida è stato scelto con saggezza. Questo è Moby Dick, approssimativamente . :-)
Arnauld

3
@Nathaniel Ci sono stati molti aggiornamenti, quindi sarebbe a malapena leggibile e non molto informativo. Ho invece aggiunto un registro delle modifiche con brevi spiegazioni sui miglioramenti.
Arnauld,

45
Penso che il tuo programma stia effettivamente facendo una traduzione perfetta in gaelico.
Beska,

1
@ Draco18s È difficile dire se questa virgola fosse una buona o una cattiva ipotesi. Se era una supposizione sbagliata, la funzione di previsione potrebbe aver legittimamente tentato di inserire una lettera dopo qualunque altra lettera che era effettivamente lì invece della virgola una volta ricevuta.
Arnauld,

91

Perl, 2 · 70525 + 326508 = 467558

Predictor

$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}

Per eseguire questo programma, è necessario questo file qui , che deve essere nominato B. (Puoi modificare questo nome file nella seconda istanza del carattere Bsopra.) Vedi sotto per come generare questo file.

Il programma utilizza una combinazione di modelli Markov essenzialmente come in questa risposta dall'utente 2699 , ma con alcune piccole modifiche. Questo produce una distribuzione per il personaggio successivo. Usiamo la teoria dell'informazione per decidere se accettare un errore o spendere bit di memoria in Bsuggerimenti per la codifica (e in tal caso, come). Usiamo la codifica aritmetica per memorizzare in modo ottimale i bit frazionari del modello.

Il programma ha una lunghezza di 582 byte (inclusa una nuova riga finale non necessaria) e il file binario Bè lungo 69942 byte, quindi in base alle regole per il calcolo del punteggio di più file , Lcalcoliamo 582 + 69942 + 1 = 70525.

Il programma richiede quasi certamente un'architettura a 64 bit (little-endian?). Sono necessari circa 2,5 minuti per l'esecuzione su m5.largeun'istanza su Amazon EC2.

Codice di prova

# 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

Il cablaggio di test presuppone che l'invio sia nel file submission.pl, ma questo può essere facilmente modificato nella seconda riga.

Confronto testuale

"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

Questo esempio (scelto in un'altra risposta ) si presenta piuttosto tardi nel testo, quindi il modello è abbastanza sviluppato da questo punto. Ricorda che il modello è aumentato di 70 kilobyte di "suggerimenti" che aiutano direttamente a indovinare i personaggi; non è guidato semplicemente dal breve frammento di codice sopra.

Generazione di suggerimenti

Il seguente programma accetta il codice di invio esatto sopra (sull'input standard) e genera il Bfile esatto sopra (sull'output 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],'}}'

Ci vuole circa il tempo necessario per eseguire l'invio, poiché esegue calcoli simili.

Spiegazione

In questa sezione, tenteremo di descrivere ciò che questa soluzione fa in modo sufficientemente dettagliato da poterti "provare a casa" da solo. La tecnica principale che differenzia questa risposta dalle altre è una parte del meccanismo di "riavvolgimento", ma prima di arrivarci, dobbiamo impostare le basi.

Modello

L'ingrediente di base della soluzione è un modello linguistico. Per i nostri scopi, un modello è qualcosa che richiede una certa quantità di testo inglese e restituisce una distribuzione di probabilità sul carattere successivo. Quando usiamo il modello, il testo inglese sarà un prefisso (corretto) di Moby Dick. Si noti che l'output desiderato è una distribuzione e non solo una singola ipotesi per il personaggio più probabile.

Nel nostro caso, utilizziamo essenzialmente il modello in questa risposta dall'utente2699 . Non abbiamo utilizzato il modello dalla risposta con il punteggio più alto (diverso dal nostro) di Anders Kaseorg proprio perché non siamo stati in grado di estrarre una distribuzione piuttosto che una singola ipotesi. In teoria, quella risposta calcola una media geometrica ponderata, ma abbiamo ottenuto risultati piuttosto scarsi quando l'abbiamo interpretata in modo troppo letterale. Abbiamo "rubato" un modello a un'altra risposta perché la nostra "salsa segreta" non è il modello ma piuttosto l'approccio globale. Se qualcuno ha un modello "migliore", dovrebbe essere in grado di ottenere risultati migliori usando il resto delle nostre tecniche.

Come osservazione, la maggior parte dei metodi di compressione come Lempel-Ziv può essere visto come un "modello di linguaggio" in questo modo, anche se si potrebbe dover socchiudere leggermente gli occhi. (È particolarmente complicato per qualcosa che trasforma una Burrows-Wheeler!) Inoltre, si noti che il modello di user2699 è una modifica di un modello di Markov; essenzialmente nient'altro è competitivo per questa sfida o forse anche per modellare il testo in generale.

Architettura generale

Ai fini della comprensione, è bello suddividere l'architettura complessiva in più pezzi. Dal punto di vista del livello più alto, deve esserci un po 'di codice di gestione dello stato. Questo non è particolarmente interessante, ma per completezza vogliamo sottolineare che ad ogni punto il programma viene chiesto per la prossima ipotesi, ha a disposizione un prefisso corretto di Moby Dick. Non utilizziamo in alcun modo le nostre ipotesi errate passate. Per motivi di efficienza, il modello di linguaggio può probabilmente riutilizzare il suo stato dai primi N caratteri per calcolare il suo stato per i primi (N + 1) caratteri, ma in linea di principio potrebbe ricalcolare le cose da zero ogni volta che viene invocato.

Mettiamo da parte questo "driver" di base del programma e sbirciano dentro la parte che indovina il personaggio successivo. Aiuta concettualmente a separare tre parti: il modello linguistico (discusso sopra), un file "suggerimenti" e un "interprete". Ad ogni passo, l'interprete chiederà al modello di lingua una distribuzione per il carattere successivo e possibilmente leggerà alcune informazioni dal file dei suggerimenti. Quindi combinerà queste parti in un'ipotesi. Le informazioni contenute nel file dei suggerimenti e il modo in cui vengono utilizzate verranno spiegate in seguito, ma per ora aiuta a mantenere separate queste parti mentalmente. Si noti che dal punto di vista dell'implementazione, il file dei suggerimenti è letteralmente un file separato (binario) ma potrebbe essere stato una stringa o qualcosa memorizzato all'interno del programma. Per approssimazione,

Se si utilizza un metodo di compressione standard come bzip2 come in questa risposta , il file "suggerimenti" corrisponde al file compresso. L '"interprete" corrisponde al decompressore, mentre il "modello di linguaggio" è un po' implicito (come menzionato sopra).

Perché usare un file di suggerimento?

Facciamo un semplice esempio per analizzare ulteriormente. Supponiamo che il testo sia Ncomposto da caratteri lunghi e ben approssimati da un modello in cui ogni carattere è (indipendentemente) la lettera Econ probabilità leggermente inferiore alla metà, in Tmodo simile con probabilità leggermente inferiore alla metà e Acon probabilità 1/1000 = 0,1%. Supponiamo che non siano possibili altri personaggi; in ogni caso, Aè abbastanza simile al caso di un personaggio mai visto prima di punto in bianco.

Se operassimo nel regime L 0 (come fanno la maggior parte, ma non tutte, delle altre risposte a questa domanda), non c'è strategia migliore per l'interprete che scegliere una delle Ee T. In media, otterrà circa la metà dei caratteri corretti. Quindi E ≈ N / 2 e il punteggio ≈ N / 2 anche. Tuttavia, se utilizziamo una strategia di compressione, possiamo comprimere a poco più di un bit per carattere. Dato che L viene conteggiato in byte, otteniamo L ≈ N / 8 e quindi segniamo ≈ N / 4, due volte meglio della strategia precedente.

Raggiungere questo tasso di poco più di un bit per carattere per questo modello è leggermente non banale, ma un metodo è la codifica aritmetica.

Codifica aritmetica

Come è noto, una codifica è un modo per rappresentare alcuni dati usando bit / byte. Ad esempio, ASCII è una codifica a 7 bit / carattere del testo inglese e dei caratteri correlati, ed è la codifica del file Moby Dick originale in esame. Se alcune lettere sono più comuni di altre, una codifica a larghezza fissa come ASCII non è ottimale. In una situazione del genere, molte persone cercano la codifica di Huffman . Questo è ottimale se si desidera un codice fisso (senza prefisso) con un numero intero di bit per carattere.

Tuttavia, la codifica aritmetica è ancora migliore. In parole povere, è in grado di usare bit "frazionari" per codificare le informazioni. Ci sono molte guide alla codifica aritmetica disponibili online. Salteremo i dettagli qui (soprattutto dell'implementazione pratica, che può essere un po 'complicata dal punto di vista della programmazione) a causa delle altre risorse disponibili online, ma se qualcuno si lamenta, forse questa sezione può essere arricchita di più.

Se uno ha il testo effettivamente generato da un modello di linguaggio noto, la codifica aritmetica fornisce una codifica essenzialmente ottimale del testo da quel modello. In un certo senso, questo "risolve" il problema di compressione per quel modello. (Quindi, in pratica, il problema principale è che il modello non è noto e alcuni modelli sono migliori di altri nel modellare il testo umano.) Se non è stato permesso di fare errori in questo contesto, allora nella lingua della sezione precedente , un modo per produrre una soluzione a questa sfida sarebbe stato quello di utilizzare un codificatore aritmetico per generare un file "suggerimenti" dal modello di linguaggio e quindi utilizzare un decodificatore aritmetico come "interprete".

In questa codifica essenzialmente ottimale, finiamo per spendere -log_2 (p) bit per un personaggio con probabilità p, e il bit-rate complessivo della codifica è l' entropia di Shannon . Ciò significa che un carattere con probabilità vicino a 1/2 impiega circa un bit per codificare, mentre uno con probabilità 1/1000 impiega circa 10 bit (perché 2 ^ 10 è approssimativamente 1000).

Ma la metrica del punteggio per questa sfida è stata ben scelta per evitare la compressione come strategia ottimale. Dovremo trovare un modo per fare alcuni errori come un compromesso per ottenere un file di suggerimenti più breve. Ad esempio, una strategia che si potrebbe provare è una semplice strategia di ramificazione: generalmente possiamo provare a usare la codifica aritmetica quando possiamo, ma se la distribuzione di probabilità dal modello è "cattiva" in qualche modo indoviniamo semplicemente il carattere più probabile e non prova a codificarlo.

Perché commettere errori?

Analizziamo l'esempio di prima per motivare il motivo per cui potremmo voler commettere errori "intenzionalmente". Se usiamo la codifica aritmetica per codificare il carattere corretto, impiegheremo all'incirca un bit nel caso di un Eo T, ma circa dieci bit nel caso di un A.

Nel complesso, questa è una codifica abbastanza buona, che spende poco più di un po 'per personaggio, anche se ci sono tre possibilità; fondamentalmente, Aè abbastanza improbabile e non finiamo per spendere troppo spesso i suoi corrispondenti dieci bit. Tuttavia, non sarebbe bello se potessimo semplicemente fare un errore nel caso di un A? Dopotutto, la metrica per il problema considera 1 byte = 8 bit di lunghezza equivalente a 2 errori; quindi sembra che si dovrebbe preferire un errore invece di spendere più di 8/2 = 4 bit su un personaggio. Trascorrere più di un byte per salvare un errore sembra decisamente non ottimale!

Il meccanismo di "riavvolgimento"

Questa sezione descrive il principale aspetto intelligente di questa soluzione, che è un modo per gestire ipotesi errate senza costi di lunghezza.

Per il semplice esempio che abbiamo analizzato, il meccanismo di riavvolgimento è particolarmente semplice. L'interprete legge un bit dal file dei suggerimenti. Se è uno 0, indovina E. Se è un 1, indovina T. La prossima volta che viene chiamato, vede qual è il personaggio corretto. Se il file di suggerimento è impostato correttamente, possiamo assicurarci che, nel caso di un Eo T, l'interprete indovini correttamente. Ma che dire A? L'idea del meccanismo di riavvolgimento è semplicemente di non codificare Aaffatto . Più precisamente, se l'interprete in seguito apprende che il carattere corretto era un A" riavvolge metaforicamente il nastro": restituisce il bit letto in precedenza. Il bit che legge intende codificare EoT, ma non ora; sarà usato in seguito. In questo semplice esempio, ciò significa fondamentalmente che continua a indovinare lo stesso personaggio ( Eo T) fino a quando non lo fa bene; poi legge un altro po 'e continua.

La codifica per questo file di suggerimenti è molto semplice: trasforma tutte le Es in 0 bit e Ts in 1 bit, il tutto ignorando Acompletamente s. Dall'analisi alla fine della sezione precedente, questo schema fa degli errori ma riduce il punteggio complessivo non codificando nessuno dei messaggi A. Come effetto minore, risparmia anche sulla lunghezza del file dei suggerimenti, perché finiamo per usare esattamente un bit per ciascuno Ee Tinvece di leggermente più di un bit.

Un piccolo teorema

Come decidiamo quando fare un errore? Supponiamo che il nostro modello ci dia una distribuzione di probabilità P per il prossimo personaggio. Separeremo i possibili caratteri in due classi: codificati e non codificati . Se il carattere corretto non è codificato, finiremo per utilizzare il meccanismo di "riavvolgimento" per accettare un errore senza costi di lunghezza. Se il carattere corretto è codificato, useremo qualche altra distribuzione Q per codificarlo usando la codifica aritmetica.

Ma quale distribuzione Q dovremmo scegliere? Non è troppo difficile vedere che tutti i caratteri codificati dovrebbero avere una probabilità maggiore (in P) rispetto ai caratteri non codificati. Inoltre, la distribuzione Q dovrebbe includere solo i caratteri codificati; dopo tutto, non stiamo codificando gli altri, quindi non dovremmo "spendere" entropia su di essi. È un po 'più complicato vedere che la distribuzione di probabilità Q dovrebbe essere proporzionale a P sui caratteri codificati. Mettere insieme queste osservazioni significa che dovremmo codificare i caratteri più probabili, ma forse non quelli meno probabili, e che Q è semplicemente ridimensionato sui caratteri codificati.

Si scopre inoltre che esiste un teorema interessante riguardo a quale "cutoff" si dovrebbe scegliere per i caratteri di codifica: è necessario codificare un carattere fintanto che è almeno 1 / 5,393 con la probabilità degli altri caratteri codificati combinati. Questo "spiega" la comparsa della costante apparentemente casuale 5.393più vicina alla fine del programma sopra. Il numero 1 / 5.393 ≈ 0.18542 è la soluzione all'equazione -p log (16) - p log p + (1 + p) log (1 + p) = 0 .

Forse è una buona idea scrivere questa procedura nel codice. Questo frammento è in 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;
}

Mettere tutto insieme

La sezione precedente è purtroppo un po 'tecnica, ma se mettiamo insieme tutti gli altri pezzi, la struttura è la seguente. Ogni volta che viene richiesto al programma di prevedere il carattere successivo dopo un determinato carattere corretto:

  1. Aggiungi il carattere corretto al prefisso corretto noto di Moby Dick.
  2. Aggiorna il modello (Markov) del testo.
  3. La salsa segreta : se l'ipotesi precedente era errata, riavvolgi lo stato del decodificatore aritmetico al suo stato prima dell'ipotesi precedente!
  4. Chiedi al modello di Markov di prevedere una distribuzione di probabilità P per il personaggio successivo.
  5. Trasforma P in Q usando la subroutine della sezione precedente.
  6. Chiedere al decodificatore aritmetico di decodificare un carattere dal resto del file dei suggerimenti, secondo la distribuzione Q.
  7. Indovina il personaggio risultante.

La codifica del file dei suggerimenti funziona in modo simile. In tal caso, il programma sa qual è il prossimo personaggio corretto. Se è un carattere che dovrebbe essere codificato, allora ovviamente si dovrebbe usare l'encoder aritmetico su di esso; ma se è un carattere non codificato, semplicemente non aggiorna lo stato dell'encoder aritmetico.

Se capisci il background teorico dell'informazione come distribuzioni di probabilità, entropia, compressione e codifica aritmetica ma hai provato e non hai capito questo post (tranne perché il teorema è vero), faccelo sapere e possiamo provare a chiarire le cose. Grazie per aver letto!


8
Caspita, risposta impressionante. Presumo che sia necessario un codice aggiuntivo per generare il Bfile? In tal caso, puoi includerlo nella tua risposta?
Nathaniel,

8
Eccellente! La prima (e finora l'unica) risposta a superare la barriera del punteggio di 500k.
ShreevatsaR,

5
"tersed the shite whale" omg sto piangendo
Phill,

5
Dal momento che non sono state pubblicate nuove risposte durante il periodo di bounty, la assegnerò alla tua risposta, sia come miglior punteggio sia come approccio più sofisticato. Se mai avessi tempo, apprezzerei davvero una spiegazione più approfondita di come funziona questa risposta, ovvero che cos'è esattamente l'algoritmo?
Nathaniel,

2
@Nathaniel: ho aggiunto una spiegazione a questo post. Fammi sapere se pensi che sia abbastanza dettagliato per riprodurre tu stesso la soluzione.
A. Rex,

77

Python 3, 2 · 267 + 510193 = 510727

Predictor

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]

Questo utilizza una combinazione bayesiana ponderata dell'ordine 0,…, 16 modelli Markov, con pesi [1, 6, 12, 30, 65, 99, 87, 117, 118, 89, 95, 118, 96, 184, 126, 219, 126].

Il risultato non è molto sensibile alla selezione di questi pesi, ma li ho ottimizzati perché potevo, usando lo stesso algoritmo di arrampicata in salita che ho usato nella mia risposta a "Metti insieme una maggioranza del Senato" , dove ogni mutazione del candidato è solo un incremento di ± 1 ad un singolo peso.

Codice di prova

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
Lo strumento giusto per il lavoro. Ottimo punteggio. Ben fatto.
agtoever

1
Possibile chiarimento: b"\0\3\6\r\34'&-20'\22!P\n[\26"è la rappresentazione ASCII dei pesi, in cui piccoli valori non stampabili sono sfuggiti in ottale.
Cœur l'

Ho aggiornato la domanda con una versione del file in cui il testo non è racchiuso - potresti provare a rieseguire il codice su questo (potrebbe fare leggermente meglio)
Nathaniel,

3
Grazie per quella spiegazione - se potessi modificare una sinossi nella domanda sarebbe fantastico. (L'esperienza con la mia precedente sfida Paint Starry Night è che queste procedure di ottimizzazione sono la parte più interessante delle risposte, quindi è molto meglio se le risposte includono il codice utilizzato per farlo e una spiegazione. Ho incluso una regola in entrambi sfide dicendo che dovrebbero.)
Nathaniel,

1
@Christoph La mia combinazione di modelli è in realtà una media geometrica ponderata. Ma la media di PAQ nel dominio logistico è leggermente diversa: dovrò vedere se è meglio.
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

Provalo online!

Una funzione che utilizza variabili globali. Impara come va, costruendo un modello a livello di parola: dato ciò che è visto finora in questa parola , qual è il prossimo personaggio più comune? Man mano che arriva più input, impara abbastanza bene le parole comuni dal testo e impara anche il carattere più comune per iniziare la parola successiva .

Per esempio:

  • Se ciò che è stato visto finora è 'Captai', prevede una "n"
  • Se è "Capitano", predice uno spazio
  • Se è l'inizio di una parola e l'ultima parola era "Capitano", prevede una "A"
  • Se la parola finora è 'A', prevede una 'h' (e quindi 'a' e 'b'; allo stesso modo per 'C').

Non va molto bene all'inizio, ma alla fine ci sono grandi parti di parole reali che escono. L'opzione di fallback è uno spazio e dopo un singolo spazio è una "a", a meno che la lettera precedente non sia una "nedtfo", una cifra o un trattino o apostrofo. Prevede anche in modo aggressivo le interruzioni di riga dopo 71 caratteri, o se uno spazio era previsto dopo 66. Entrambi erano appena sintonizzati sui dati ("t" è molto più comune dopo uno spazio, ma è già stato più spesso previsto, quindi " a "è un'ipotesi migliore al di fuori di questi sei casi speciali).

Imparare quali coppie di parole sono state messe insieme e preconfigurare la mappatura si è rivelato non utile.


Si finisce con un testo come questo:

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!

che corrisponde a questa parte dell'input:

tutt'intorno a lui.

"L'ho visto quasi nello stesso istante, signore, che ha fatto il Capitano Achab, e ho gridato," disse Tashtego.

"Non lo stesso istante; non lo stesso - no, il doblone è mio, il destino mi ha riservato il doblone. Solo io; nessuno di voi avrebbe potuto sollevare prima la balena bianca. Lì lei soffia! - lì lei soffia! - -qui lei soffia!

Puoi vedere dove i nomi propri in particolare escono piuttosto bene, ma anche le estremità delle parole sono per lo più giuste. Quando viene visto "dou", si aspetta "dubbio", ma una volta che appare la "l" si ottiene "doblone".

Se lo esegui una seconda volta con lo stesso modello appena costruito, ottiene immediatamente un altro 92k corretto (51,7% -> 59,3%), ma è sempre poco meno del 60% dalla seconda iterazione in poi.


Il codice di misurazione si trova nel collegamento TIO, oppure ecco una versione leggermente migliore:

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 ha l'output indovinato alla fine.


3
Questo è un approccio eccellente!
Skyler,

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

1
+1 perché questo approccio mi ha ricordato l'algoritmo di compressione LZW.
Marcos,

25

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

Grazie a @Quentin per aver salvato 217 byte!

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;}

Una soluzione molto semplice che, dato un carattere, genera semplicemente il carattere che appare più frequentemente dopo il carattere di input.

Verifica il punteggio con:

#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;
}

Modifica: l' utilizzo whale2.txtconsente di ottenere un punteggio migliore.


5
Puoi tradurre questo array in una stringa letterale e incorporarlo direttamente al posto di Lsalvare un mucchio di caratteri :)
Quentin

@Quentin Grazie! Ora mi chiedo perché non ci abbia pensato in primo luogo ...
Steadybox il

20

Python, 2 * 516 + 521122 = 522154

Algoritmo:

Ancora un'altra presentazione di Python, questo algoritmo calcola la lettera successiva più probabile osservando sequenze di lunghezza 1, ..., l. Viene utilizzata la somma delle probabilità e ci sono alcuni trucchi per ottenere risultati migliori.

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')]

risultati:

Principalmente incomprensibile, anche se puoi vederlo riprendere dalla frase occasionale, come "Father 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

Codice di prova:

Abbastanza semplice, genera alcuni esempi del testo in diversi punti. Utilizza whale2.txt, in quanto ciò evita una logica aggiuntiva per calcolare le nuove righe.

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
Benvenuti nel sito! Questa è una fantastica prima presentazione. :)
DJMcMayhem

@DJMcMayhem, grazie per il benvenuto. Mi è piaciuto guardare per un po 'di tempo, questo è il primo concorso a catturare la mia attenzione per un ingresso.
user2699

19

C (gcc) , 679787 652892

84 76 byte, 679619 652740 ipotesi errate

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];}

Provalo online!

Aggiornamento: ~ 27000 punti con il file aggiornato, 16 punti (8 byte) con una funzione di golf migliore.

Spiegazione

Il modo in cui funziona è che mentre il codice scorre attraverso il testo, memorizza l'ultimo carattere che ha terminato una determinata sequenza di 4 caratteri e restituisce quel valore. Un po 'simile all'approccio di Arnauld sopra, ma si basa sulla probabilità intrinseca di due sequenze di 4 caratteri che terminano allo stesso modo.

De-giocato a 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.
}

... Il collegamento TIO è inutile. Quindi la funzione restituisce il valore dell'ultima assegnazione?
user202729

Vorrei modificare la risposta con una spiegazione, quindi :)

1
@Rogem Ho aggiunto una versione decodificata (cosa che ho fatto perché non sono riuscita a seguirla) - si spera che questo non ti dia fastidio, ma per favore torna indietro se lo desideri.
Adam Davis,

@AdamDavis sulla maggior parte delle implementazioni di C, tutte le variabili globali iniziano da zero. È un comportamento indefinito, quindi viene utilizzato solo nel code-golf.
NieDzejkob,

1
@NieDzejkob Ah, hai ragione, grazie! "ANSI-C richiede che tutte le variabili statiche / globali non inizializzate debbano essere inizializzate con 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

seguito da whale2.txt compresso bzip2 con il primo byte mancante

Ignora il suo input; genera la risposta corretta. Ciò fornisce una linea di base su un'estremità; daniero fornisce una linea di base sull'altra estremità.

Script del costruttore:

#!/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

Cablaggio di prova I / O (tcc; tagliare la prima riga per gcc). Questo cablaggio di prova può essere utilizzato da chiunque su una piattaforma adatta che presenta un programma completo che prevede I / O di lettura / scrittura. Utilizza I / O byte alla volta per evitare i cheat. Il programma figlio deve scaricare l'output dopo ogni byte per evitare il blocco.

#!/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
Penso che ciò che sta chiedendo sia: in che modo ciò non viola 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.clausola?

8
@Rogem I dati compressi vengono inseriti dopo ciò che viene mostrato qui e l'accesso al codice stesso.
user202729

4
La domanda dice: "Il tuo invio sarà un programma o una funzione (ecc.) Che verrà chiamato o invocato più volte. Quando viene chiamato per il nthtempo, riceverà l'ennesimo carattere di whale.txto whale2.txte dovrà emettere la sua ipotesi per il (n+1)thcarattere." - Come viene soddisfatto questo requisito? Il codice visualizza l'intero testo whale.txtogni volta che viene eseguito.
axiac

1
@axiac "tutto va bene, purché il tuo programma fornisca sempre un byte di output prima di ricevere il prossimo byte di input."
user202729

5
@axiac dato il cablaggio di prova, sono felice di considerare l'invio del programma a un byte da STDIN come "chiamata o invocazione". L'importante è che il programma restituisca un byte di output dopo ogni byte di input, cosa che effettivamente fa questo, quando viene eseguito attraverso il cablaggio di test. Come dice la domanda, "tutto va bene, purché il programma dia sempre un byte di output prima di ricevere il successivo byte di input".
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])

Provalo online!


... La ///risposta che stampa uno spazio ottiene 10 voti, mentre il mio codice può ottenere solo 3 ...

Spiegazione:

Per ogni personaggio, il programma:

  • aumentare frequency[prev][char]
  • Trova il personaggio che appare più volte in frequency[char]
  • e l'output.

  • Codice non registrato nel collegamento TIO, commentato.
  • Il codice è di 131 byte.
  • Il codice eseguito sul mio computer riporta:
879504 / 1215235
Time: 62.01348257784468

che hanno il punteggio totale

2*131 + 879504 = 879766

Poiché non è possibile caricare un file di grandi dimensioni su TIO (tranne chiedere a Dennis), l'esempio eseguito nel collegamento TIO esegue il programma solo per una piccola parte del testo.

Rispetto alla risposta precedente, questa ha 362 caratteri più errati, ma il codice è più corto di 255 byte. Il moltiplicatore fa sì che il mio invio abbia un punteggio più basso.


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:' ';}}

Questo approccio utilizza una tabella di ricerca per apprendere il carattere più comune che segue una determinata stringa. I tasti della tabella di ricerca hanno un massimo di 4 caratteri, quindi la funzione aggiorna prima la tabella di ricerca con il carattere corrente, quindi controlla semplicemente quale personaggio ha più probabilità di accadere dopo i 4 precedenti, incluso quello corrente . Se quei 4 caratteri non vengono trovati nella tabella di ricerca, stampa uno spazio.

Questa versione utilizza il whale2.txtfile, poiché migliora notevolmente il numero di ipotesi riuscite.

Di seguito è riportato il codice utilizzato per testare 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();
    }
}

Il codice viene eseguito in appena 2 secondi. Solo per la cronaca, questo è ciò che ottengo quando modifico la dimensione delle chiavi della tabella di consultazione (include i risultati di una seconda esecuzione senza ripristinare il modello):

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

Sarebbe interessante sapere perché una dimensione chiave di 4 caratteri è la scelta migliore in questo algoritmo.

Confronto testuale

Originale:

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

ricreato:

"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 

ipotesi:

"_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__

Registro delle modifiche

  • 569279 - modificato in whale2.txte quindi rimosso l'ottimizzazione.
  • 577366 - ottimizzato con codice che ha tentato di indovinare quando restituire un feed di riga.
  • 590354 - versione originale.

4
Grazie per aver mostrato la varianza mentre modifichi la dimensione della chiave e la soglia della colonna!
Jeremy Weirich,

Ho aggiornato la domanda con una versione del file in cui il testo non è racchiuso - probabilmente puoi salvare alcuni punti usando quello
Nathaniel

@Nathaniel lo fa davvero. Ho aggiornato la risposta.
Charlie,

È possibile salvare alcuni byte usando var invece di dichiarare i tipi.
Ed T,

1
Man mano che la dimensione della chiave aumenta, il numero di hit versi miss diminuirà e quindi verranno generati più spazi quando una chiave più corta potrebbe aver indovinato il carattere corretto. Man mano che le dimensioni della chiave diminuiscono, le singole ipotesi sono meno precise per i segmenti corrispondenti. Sospetto che sia per questo che una lunghezza di quattro sia ottimale. Se hai mantenuto chiavi di più lunghezze e hai usato corrispondenze più brevi quando quelle più lunghe non sono disponibili, mi aspetto che il tasso di successo (e quindi il punteggio) sarà notevolmente migliorato a lunghezze di chiave più lunghe.
Jeffrey L Whitledge,

11

Java 7, 1995 caratteri, (1995 * 2 + 525158) 529148

Java fa schifo per programmi di piccole dimensioni. Comunque, ho provato diversi approcci estremamente complessi e difficili che hanno prodotto risultati sorprendentemente schifosi. In seguito sono tornato indietro e ho semplicemente adottato un approccio semplice, che ha portato a una dimensione del programma inferiore e risultati migliori.

Questo approccio è in realtà estremamente semplice. Alimenta ciecamente i precedenti caratteri x (oltre a tutte le sottostringhe di quei caratteri) in una tabella hash, mappata al carattere corrente. Quindi tiene traccia di quali schemi prevedono con precisione il carattere corrente. Se si incontrano più volte modelli che precedono determinati personaggi, riescono a prevederlo. Dà la precedenza a stringhe più lunghe e dà la precedenza al carattere che più spesso segue una determinata stringa. Questo algoritmo non sa nulla del tipo di documento o della lingua inglese.

Ho deciso di usare 9 caratteri e tentare di abbinare parole intere all'interno di quei 9 caratteri precedenti, quando possibile. Quando non si tenta di eseguire la corrispondenza delle parole all'interno delle stringhe, la lunghezza ottimale è di 6 caratteri, producendo diverse migliaia di previsioni errate.

Un'osservazione interessante è stata che l'utilizzo di 20 caratteri ha prodotto cattive previsioni la prima volta, ma un'accuratezza del 99,9 percento sui passaggi successivi. L'algoritmo era sostanzialmente in grado di memorizzare il libro in blocchi sovrapposti da 20 byte, e questo era abbastanza distinto da permettergli di ricordare l'intero libro un personaggio alla volta.

  • (1950 * 2 + 532919) 536819
  • (2406 * 2 + 526233) 531045 verifica la punteggiatura per fare ipotesi migliori
  • (1995 * 2 + 525158) 529148 altri aggiustamenti, golfò un po 'di verbosità

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); }

È un punteggio abbastanza buono per un linguaggio così prolisso.
DJMcMayhem

1
Ho pensato che valesse la pena di provare, dato che le dimensioni del file offrono molto spazio di miglioramento rispetto alle dimensioni del programma.
Jim W,

3
Questo non è compilabile in Java 7 (o qualsiasi versione Java, per quello che vale). Per favore, aggiusteresti il ​​tuo codice? Una volta fatto, giocherò volentieri in modo da migliorare il tuo punteggio.
Olivier Grégoire,

Non testato, ma questo dovrebbe essere esattamente lo stesso codice leggermente golfato: 950 byte . Il tuo codice attuale conteneva alcuni errori, quindi non sono sicuro di aver compilato correttamente tutto. Ancora una volta, non testato, quindi basta confrontare le versioni per vedere cosa ho cambiato / rinominato e vedere se tutto funziona ancora come il codice originale. Si può sicuramente giocare ancora un po 'a golf.
Kevin Cruijssen,

Merda, l'ho fatto mentre mi annoiavo nel mio vecchio lavoro e non portavo il codice con me. Dovrò dare un'occhiata per vedere dov'è l'errore di battitura.
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

Ho provato questo in modo indipendente, ma ho finito con quella che è effettivamente una versione inferiore della risposta di Michael Homer. Spero che ciò non renda la mia risposta completamente obsoleta.

Questo crea nel tempo un dizionario di parole (definito rozzamente come stringhe terminate da o \n, maiuscole e minuscole inclusa). Quindi cerca in questo dizionario le parole che iniziano con ciò che finora conosce della parola corrente, ordina l'elenco risultante per frequenza di occorrenza (lentamente) e indovina che il carattere successivo è il carattere successivo nella parola di corrispondenza più comune. Se abbiamo già la parola di corrispondenza più comune o non esiste più una parola di corrispondenza, viene restituita .

Crea anche un dizionario disgustosamente inefficiente di coppie di parole. Quando si colpisce un limite di parole, si suppone che il carattere successivo sia la prima lettera della seconda parola nella coppia di parole corrispondente più comune, o tse non vi è corrispondenza. Non è molto intelligente, però. Di seguito Moby, il programma indovina correttamente il personaggio successivo D, ma poi dimentica tutto del contesto e di solito finisce per chiamare la balena "Moby Duck" (perché la parola "olandese" sembra essere più frequente nella prima metà del testo ). Sarebbe facile risolvere questo problema dando priorità alle coppie di parole rispetto alle singole parole, ma mi aspetto che il guadagno sia marginale (dal momento che di solito è corretto dal terzo carattere in poi, e le coppie di parole non sono così utili in primo luogo).

Potrei sintonizzarlo per abbinare meglio il testo fornito, ma non penso che sintonizzare manualmente l'algoritmo basato sulla conoscenza precedente dell'input sia davvero nello spirito del gioco, quindi, oltre a scegliere t come carattere di fallback dopo uno spazio ( e probabilmente non avrei dovuto farlo neanche io), l'ho evitato. Ho ignorato la lunghezza della linea nota del file di input e invece ho inserito \nogni 13 spazi: questa è quasi certamente una corrispondenza molto scadente, l'intenzione principale era di mantenere ragionevole la lunghezza della linea piuttosto che quella dell'input.

Il codice non è esattamente veloce (~ 2 ore sulla mia macchina), ma nel complesso ottiene circa la metà dei caratteri giusti (49%). Mi aspetto che il punteggio sarebbe leggermente migliore se eseguito whale2.txt, ma non l'ho fatto.

L'inizio dell'output è simile al seguente:

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

ma alla fine, sembra un po 'più simile a ... qualcosa. Il mio passaggio preferito da quasi la fine del libro,

e poiché nessuno dei due può essere mio, permettimi di rimorchiarlo a pezzi, mentre ti inseguo, anche se legato a te, maledetta balena! COSÌ, rinuncio alla lancia! "

viene fuori come

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 "

Ciò avrebbe reso The Wrath of Khan molto più confuso. E "solitario" → "formicolio" è una sostituzione particolarmente soddisfacente.

Modifica: salvato un byte eliminando uno spazio estraneo

punteggio

#! /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

Questo esegue il programma per il testo di Moby Dick e genera il testo "previsto" su stdout e abusa di stderr per scrivere la partitura. Consiglio di reindirizzare l'output su un file.


2
Benvenuti in PPCG!
Martin Ender,

1
Non lambda i:i[1]sarebbe più economico che occuparsene operator?
Draconis,

@Draconis Quasi certamente.
georgewatson,

9

C ++, 2 · 62829 + 318786 = 444444

Per eseguire questo programma, è necessario questo file qui , che deve essere nominato C.

Il programma utilizza la stessa combinazione di modelli Markov della nostra risposta precedente . Come prima, questa combinazione è essenzialmente il modello da questa risposta dell'utente 2699 , ma con alcune piccole modifiche.

Visto che questa risposta utilizza esattamente lo stesso modello di prima, il miglioramento è un meccanismo teorico dell'informazione migliore del meccanismo di "riavvolgimento" descritto in precedenza. Ciò consente di fare meno errori pur avendo una lunghezza combinata più piccola. Il programma in sé non è molto giocato a golf perché non è il principale contribuente al punteggio.

Il programma ha una lunghezza di 2167 byte (comprese tutte le schede per il rientro e molti altri caratteri non necessari, ma prima del codice di test) e il file binario Cè lungo 60661 byte, quindi in base alle regole per il calcolo del punteggio di più file , si Lottiene un punteggio di 2167 + 60661 + 1 = 62829.

Il programma richiede circa 8 minuti per essere eseguito su m5.4xlargeun'istanza su Amazon EC2 e utilizza poco più di 16 GB di memoria. (Questo uso eccessivo della memoria non è necessario - non l'abbiamo nemmeno ottimizzato.)

#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 byte, errori 526092 (usando whale2.txt). Questo è sicuramente in grado di migliorare ulteriormente, ma ha raggiunto la fase "abbastanza buona da postare".

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'idea è di memorizzare le frequenze di tutte le corse di 2, 3, 4, ..., 10 caratteri. Per ognuna di queste lunghezze L, controlliamo se i caratteri L-1 più recenti corrispondono a un modello memorizzato; in tal caso, la nostra ipotesi g L è il personaggio successivo più frequente seguendo questo schema. In questo modo raccogliamo fino a nove ipotesi. Per decidere quale ipotesi utilizzare, ponderiamo la frequenza di ogni modello per la sua lunghezza all'ottava potenza. Viene scelta la congettura con la più grande somma di frequenze ponderate. Se non ci sono schemi corrispondenti, indoviniamo lo spazio.

(La lunghezza massima del modello e l'esponente di ponderazione sono stati scelti da tentativi ed errori per fornire il minor numero di ipotesi errate.)

Ecco la mia versione di work-in-progress non modificata:

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

E il cablaggio di prova:

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

Ecco alcuni esempi di output da vicino all'inizio del testo. Già cominciamo a vedere la capacità di rifinire parole comuni, dopo aver visto la loro prima lettera ( in, to, and, by, anche, a quanto pare, 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 

Verso la fine, ci sono ancora molti errori, ma anche molte sequenze molto buone ( shmage seashawksad esempio).

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

È interessante osservare alcuni degli errori e indovinare quale parola l'algoritmo "si aspettava". Ad esempio, dopo sail, il programma prevede entrambe le volte - oper sailor, presumo. O ancora, dopo che , asi aspetta - nprobabilmente a causa dell'occorrenza comune di , and.


changelog:

  • 274 * 2 + 526092 = 526640 Golfato l'algoritmo, al costo di alcuni errori extra
  • 306 * 2 + 526089 = 526701 Versione originale

6

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

Cerca le parole corrispondenti ai caratteri precedenti da un elenco di  tutte le  parole utilizzate nel testo, ordinate in base al numero delle loro occorrenze.

Codice:

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"

Dati: https://www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0

Suite di test:

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

Modifica: l' utilizzo whale2.txtconsente di ottenere un punteggio migliore.


5

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

Ancora un altro invio di frequenza prefisso. Corri whale2.txte ottieni un punteggio simile (leggermente peggiore) rispetto ad altri.

#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]];}

Questo trova avidamente la stringa più lunga che inizia con un suffisso della storia e, se ci sono più candidati, pareggi con stringhe più brevi.

Per esempio: se gli ultimi 7 personaggi sono abcdefgh, e la stringa abcdefghie abcdefghjcompare con il maggior frequenza in tutte le stringhe della forma abcdefgh*, l'uscita sarà uno io j, tie-break con i suffissi più corte ( bcdefgh, cdefgh, ...).

Per ragioni sconosciute, niente di più di 7 e il mio computer non ha abbastanza RAM per eseguirlo. Anche con 7, ho bisogno di chiudere tutti i browser web per eseguirlo.


Codice di prova:

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]];
}

Esempio di output:

~ ~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 

Questo è vicino alla fine del testo. La maggior parte delle parole lunghe si prevede abbastanza accuratamente ( 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

Le maiuscole non sembrano buone.


Trie sembra consumare più memoria di quanto mi aspettassi ...
user202729

... e anche più difficile da implementare.
user202729

4

Python 2, 756837

Usa qualcosa che potrebbe essere catene di 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
Spiegazione rapida: viene zlib.decompress('...')valutato {'G?':' ', 'G;':' ','G"':' ',.......}ed aè un dizionario che mappa da 2 caratteri a 1 carattere. Fondamentalmente variante di 2 caratteri della risposta di Steadybox .
user202729

1
Come posso vedere, il valore letterale è di 17780 byte. Puoi ridurlo a 11619 caratteri rimuovendo gli spazi bianchi nel contenuto decompresso, risparmiando 12322 byte. (se ho contato correttamente) Inoltre ... la conversione di codici di escape esadecimali in caratteri grezzi effettivi può far risparmiare ancora più byte.
user202729

Come posso pubblicare qualcosa qui se si tratta di byte non elaborati?
Skyler,

1
xxd, hexdump, uuencode, O simili
Peter Taylor

@ user202729 Basta notare che il codice Python non può contenere byte NUL non elaborati effettivi.
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)

Esempio:

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

Utilizza tre file ausiliari pre-calcolati:

  • seqwords contiene le 236 parole più comuni.
  • wordsseq contiene un sequece compresso con LZMA di queste parole e, per tutte le parole non tra le 236 più comuni, la lunghezza.
  • dicttriescontiene, per ogni lunghezza di parole, un albero decisionale che contiene tutte le parole rimanenti. Da questi tentativi, le voci vengono selezionate man mano che procediamo.

In questo modo, otteniamo un tasso di errore significativamente inferiore rispetto a tutti gli altri schemi con perdite; sfortunatamente, il wordsseqfile è ancora troppo grande per essere competitivo.

Ecco una versione completa che crea i file e fa l'analisi:

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;

Allo stato attuale questa soluzione è WIP e quindi non rigata. Inoltre, considerando che la dimensione effettiva del codice ha a malapena alcun impatto sul punteggio, ho pensato di pubblicare la mia risposta prima di iniziare a micro ottimizzarla.
(Codice completo disponibile qui: https://github.com/BrainStone/MobyDickRNG - Include programma completo e ricerca seed)

Questa soluzione si basa su un RNG. Per prima cosa analizzo il testo. Creo una mappa che conta le occorrenze di due caratteri consecutivi. Quindi creo una mappa di distribuzione. Tutto questo è fatto staticamente, quindi dovrebbe essere conforme alle regole.

Quindi, mentre provo a stampare il testo, faccio una ricerca e tiro un carattere casuale tra quelli possibili. Mentre questo di solito produce risultati peggiori rispetto alla semplice emissione della lettera seguente più comune, ci sono probabilmente semi di dio che produrranno risultati migliori. Ecco perché il seme è hard coded. Attualmente sto cercando il miglior seme. E aggiornerò questa risposta una volta trovato semi migliori. Quindi rimani aggiornato!

Se qualcuno vuole cercare i semi da solo o utilizzare RNG diversi, sentiti libero di rovesciare il repository.

Metodo utilizzato per calcolare il punteggio: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15

Nota anche se il punteggio totale è il peggiore in questo momento, batte il conteggio degli errori del solo output degli spazi. E ci sono buone probabilità che il punteggio scenda, controllando più semi.

changelog

  • 24/01/2018 : risposta iniziale pubblicata.
    Semi controllati: 0-50000. Punteggio: 2305 * 2 + 1017754 = 1022364
  • 24/01/2018 : golf minimo. Aggiunto link al metodo di calcolo del punteggio.
    Semi controllati: 0-80000. Punteggio: 1920 * 2 + 1017754 = 1021594 (-770)
  • 02/02/2018 : Nuovo seme (10087702) (non è stato trovato il tempo per correggere l'invio)
    Semi controllati: 0-32000000. Punteggio: 1923 * 2 + 1017344 = 1021190 (-404)

Potresti includere un cablaggio di prova nella tua risposta che valuti il ​​punteggio?
Nathaniel,

@Nathaniel Ho collegato direttamente il codice del punteggio. Inoltre con il repository lo considereresti abbastanza?
BrainStone,

Dopo aver esaminato le regole ho notato che ho violato alcune di esse. Naturalmente aggiornerò la mia risposta una volta risolto il problema
BrainStone,

Quindi finirai per codificare il testo nel seme casuale. Vedi il linguaggio di programmazione esoterico Seed e potresti voler decodificare il programma MT19937 e battere questa risposta (se puoi).
user202729

Bella idea, ma non aiuta a ottenere un buon punteggio. +1 comunque.
user202729

3

Ruby, 1164418 (ahi)

Volevo solo vedere quanto potevo fare senza controllare altre risposte.
Non sono sicuro che ciò sia consentito perché include un valore letterale che ho generato analizzando il file, ma anche se non lo fosse non è come se fosse in pericolo di battere qualcuno.

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] : ' '}

Come ho generato x

Innanzitutto, ho generato a.txtcon il seguente:

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

Quindi ho generato a.csv:

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

Quindi l'ho analizzato xcon il seguente script Ruby:

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

Come ho segnato

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

Sono sicuro che è permesso; se hai analizzato il file, buon lavoro! Solo in questo caso il programma non è valido.
Erik the Outgolfer,

@EriktheOutgolfer> _> (inserisce silenziosamente un "(non concorrente)" nel titolo)
NO_BOOT_DEVICE

Perché? Se questo è valido, è in competizione, anche se potrebbe non battere molto. Se non è valido (ovvero, la soluzione legge dal file e non contiene solo un valore letterale), deve essere eliminata.
Erik the Outgolfer,

Hmmm. Pensavo intendessi se un programma avesse analizzato il file e non solo la soluzione.
NO_BOOT_DEVICE

1
Non riesco a leggere Ruby, ma penso che questo sia valido. Avere il valore letterale all'interno del programma è completamente soddisfacente, non è affatto un problema.
Nathaniel,

2

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

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

Provalo online!

Tabella delle frequenze piuttosto semplice. Ogni posizione nella stringa corrisponde al codice ASCII del carattere corrente (meno 10 = 0x0a = '\ n', il carattere più basso nel file) e il carattere in ciascun indice è il carattere successivo più frequente. Supponendo di aver calcolato le frequenze nel modo giusto ...

Testato con il codice dal test di user202729


Puoi salvare alcuni byte usando def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]?
Neil,

0

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

Precisione perfetta in soli 644449 byte

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

Il codice completo non può rientrare in una risposta, quindi l'ho inserito qui e ho sostituito la grande stringa binaria letterale con b '###' nel testo della risposta.

Questo viene generato con il seguente codice, dove "modified.py" è il file generato e "cheatsheet.txt" è il file whale2.txt che inizia con il secondo carattere.

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]')

Il codice può essere eseguito aggiungendo quanto segue alla fine di "modified.py". "whale2.txt" deve trovarsi nella stessa directory di "modified.py" e l'output verrà scritto in "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)

Questa risposta non accede direttamente a whale.txt o whale2.txt. Utilizza le librerie di compressione standard esistenti come esplicitamente consentito nelle regole.


potrebbe esserci un "\ r \ n" lì dentro di cui non potevo liberarmi in Windows quando li stavo contando
Legorhin

2
sì, era un errore di battitura che si propagava
Legorhin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.