Escreva Moby Dick, aproximadamente


297

Aqui está um arquivo de texto ASCII de 1.2Mb que contém o texto de Moby-Dick, de Herman Melville ; ou, a baleia . Sua tarefa é escrever um programa ou função (ou classe, etc. - veja abaixo) que receberá esse arquivo com um caractere de cada vez e, a cada passo, deve adivinhar o próximo caractere.

Este é . Sua pontuação será

2*L + E

onde Lé o tamanho do seu envio em bytes e Eo número de caracteres que ele adivinha incorretamente. A pontuação mais baixa vence.

Outras informações

Seu envio será um programa ou função (etc.) que será chamado, invocado ou enviado dados várias vezes. (1215235 vezes para ser exato). Quando ele é chamado para o n º tempo que será dado o n º personagem whale.txtou whale2.txte deve saída de seu palpite para o ( n + 1 ) th personagem. O Ecomponente de sua pontuação será o número total de caracteres que ele adivinha incorretamente.

A maioria dos envios precisará armazenar algum estado entre as chamadas, para que eles possam rastrear quantas vezes foram chamados e quais foram as entradas anteriores. Você pode fazer isso gravando em um arquivo externo, usando staticvariáveis ​​globais ou, enviando uma classe em vez de uma função, usando uma mônada de estado ou qualquer outra coisa que funcione no seu idioma. Seu envio deve incluir qualquer código necessário para inicializar seu estado antes da primeira chamada.

Seu programa deve executar deterministicamente, para que ele sempre faça as mesmas suposições com a mesma entrada (e, portanto, sempre obtenha a mesma pontuação).

Sua resposta deve incluir não apenas o envio, mas também o código que você usou para calcular a Eparte da pontuação. Isso não precisa ser escrito no mesmo idioma do seu envio e não será contado na contagem de bytes. Você é incentivado a torná-lo legível.

Com relação à interface entre o envio e o programa de cálculo de pontuação, tudo está bem, desde que o programa sempre dê um byte de saída antes de receber o próximo byte de entrada. (Portanto, por exemplo, você não pode simplesmente passar uma string contendo toda a entrada e obter uma string de volta contendo toda a saída.)

Você deve realmente executar seu programa de teste e calcular / verificar sua pontuação antes de enviar sua inscrição. Se o seu envio for muito lento para você verificar sua pontuação, ele não estará qualificado para competir, mesmo que você saiba qual seria sua pontuação em princípio.

O Lcomponente da sua pontuação será calculado de acordo com as regras usuais para desafios de golfe com código. Se o seu envio contiver vários arquivos, observe as regras de pontuação e estrutura de diretórios nesse caso. Todos os dados que seu código usa devem ser incluídos na sua Lpontuação.

Você pode importar bibliotecas existentes, mas não pode carregar outros arquivos externos, e seu código pode não acessar whale.txtouwhale2.txtarquivo de qualquer maneira que não seja a descrita acima. Você não pode carregar nenhuma rede neural pré-treinada ou outras fontes de dados estatísticos. (Não há problema em usar redes neurais, mas você deve incluir os dados de ponderação em sua submissão e contabilizá-los na contagem de bytes.) Se, por algum motivo, seu idioma ou bibliotecas incluírem um recurso que fornece parte ou todo o texto de Moby Dick , você não pode usar esse recurso. Além disso, você pode usar outros recursos internos ou de biblioteca que desejar, incluindo aqueles relacionados ao processamento, previsão ou compactação de texto, desde que façam parte do seu idioma ou de suas bibliotecas padrão. Para rotinas mais exóticas e especializadas que incluem fontes de dados estatísticos, você teria que implementá-las e incluí-las na sua contagem de bytes.

É provável que alguns envios incluam componentes que são gerados por código. Se for esse o caso, inclua na sua resposta o código que foi usado para produzi-los e explique como ele funciona . (Enquanto esse código não for necessário para executar seu envio, ele não será incluído na sua contagem de bytes.)

Por razões históricas, há duas versões do arquivo e você pode usá-las em uma resposta. Em whale2.txt(vinculado acima), o texto não é quebrado, portanto as novas linhas aparecem apenas no final dos parágrafos. No original, whale.txto texto é dividido em uma largura de 74 caracteres, portanto, você deve prever o final de cada linha e também o texto. Isso torna o desafio mais complicado, por isso whale2.txté recomendado para novas respostas. Ambos os arquivos têm o mesmo tamanho, 1215236 bytes.


Para resumir, todas as respostas devem incluir o seguinte:

  • Sua submissão em si. (O código, além de todos os arquivos de dados que ele usa - esses podem ser links se forem grandes.)
  • Uma explicação de como seu código funciona. Por favor, explique o método de E / S e também como ele prevê o próximo caractere. A explicação do seu algoritmo é importante, e boas explicações receberão recompensas de mim.
  • O código que você usou para avaliar sua pontuação. (Se isso é idêntico a uma resposta anterior, você pode simplesmente vincular a ela.)
  • Qualquer código que você usou para gerar seu envio, juntamente com uma explicação desse código. Isso inclui o código usado para otimizar parâmetros, gerar arquivos de dados etc. (isso não conta para a contagem de bytes, mas deve ser incluído na sua resposta.)

Entre os melhores

Recompensas

De tempos em tempos, oferecerei recompensas para incentivar diferentes abordagens.

O primeiro, 50 pontos, foi concedido a A. Rex pela resposta de melhor pontuação da época.

O segundo, 100 pontos, também foi concedido a A. Rex, pela mesma resposta, porque eles adicionaram uma explicação muito boa à resposta existente.

A próxima recompensa, 200 pontos , será concedida a qualquer

  • Uma resposta competitiva que utiliza uma nova técnica. (Isso será baseado no meu julgamento subjetivo, já que é meu representante que entra na recompensa, mas você pode confiar que eu seja justo. Observe que sua resposta precisa conter uma explicação suficiente para que eu entenda como funciona!) obter a pontuação máxima, ele precisa se sair razoavelmente bem em comparação com as respostas existentes. Estou particularmente interessado em encontrar soluções baseadas em redes neurais recorrentes, mas concederei a recompensa a qualquer coisa que pareça diferente o suficiente dos modelos de Markov que dominam as principais pontuações atuais.

Ou:

  • Qualquer pessoa que supere a pontuação máxima de A. Rex (atualmente 444444), usando qualquer método.

Depois que a recompensa de 200 pontos for reivindicada, provavelmente ofereço uma de 400 pontos, atualizando os requisitos de acordo.


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Dennis

9
xkcd.com/1960 parece ser uma referência a este desafio!
A.Rex

Pensei em compactar isso ... mas é um pouco longo que meu computador travou de ombros
Naruyoko 25/09

Respostas:


135

/// , 2 * 1 + 1020874 = 1020876

 

Imprime um espaço.


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Dennis

Isso é um hacker de recompensa extremamente inteligente! Você deve ser um AGI;)
Alex

97

Node.js, 2 * 224 + 524279 = 524727

Consulte o log de alterações no final deste post para atualizações de pontuação.

Uma função que recebe e retorna um 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 em um modelo PPM simples que analisa os últimos 8 caracteres para prever o próximo.

Confiamos em um padrão de comprimento L quando o encontramos pelo menos T [L] vezes, em que T é uma matriz de limites arbitrários: [1,1,2,1,2,3,5,2] . Além disso, sempre confiamos em um padrão cujo primeiro caractere corresponde [A-Z '"(].

Selecionamos o padrão confiável mais longo e retornamos a previsão com a pontuação mais alta associada a esse padrão no momento da chamada.

Notas

  • Obviamente, isso não é otimizado para velocidade, mas é executado em cerca de 15 segundos no meu laptop.

  • Se pudéssemos repetir o processo várias vezes seguidas sem redefinir o modelo, o número de erros convergiria para ~ 268000 após 5 iterações.

  • A taxa de sucesso atual da função de previsão é de ~ 56,8%. Conforme observado por @immibis nos comentários, se palpites ruins e corretos são misturados, o resultado nem é quase legível.

    Por exemplo, este trecho próximo ao final do livro:

    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.
    

    torna-se:

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

    Ao substituir palpites ruins por sublinhados, temos uma idéia melhor do que a função deu certo:

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

    Nota : O exemplo acima foi criado com uma versão anterior do código, trabalhando na primeira versão do arquivo de entrada.

Código de teste

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

Log de alterações

  • 524727 - economizou 19644 pontos ao mudar para whale2.txt (atualização do desafio)
  • 544371 - economizou 327 pontos forçando padrões começando com letra maiúscula, aspas, aspas duplas ou parêntese de abertura a serem também sempre confiáveis
  • 544698 - economizou 2119 pontos forçando padrões começando com um espaço para sempre ser confiável
  • 546817 - economizou 47 pontos ajustando os limites e jogando golfe na função de previsão
  • 546864 - salvou 1496 pontos, estendendo o comprimento máximo do padrão para 8 caracteres
  • 548360 - economizou 6239 pontos ao introduzir a noção de padrões confiáveis, com limites dependendo do comprimento
  • 554599 - economizou 1030 pontos melhorando a previsão de avanço de linha
  • 555629 - economizou 22 pontos jogando a função de previsão
  • 555651 - economizou 40 pontos jogando a função de previsão
  • 555691 - pontuação inicial

44
Para os curiosos, não, isso não produz nada como Moby Dick. É um monte de sidg tlanses,oeth to, shuld hottut tild aoersors Ch, th! Sa, yr! Sheu arinning whales aut ihe e sl he traaty of rrsf tg homn Bho dla tiasot a shab sor ty, af etoors tnd hocket sh bts ait mtubb tiddin tis aeewnrs, dnhost maundy cnd sner aiwt d boelh cheugh -aaieiyns aasiyns taaeiins! th, tla. Ele consegue obter algumas palavras completas às vezes. Like whales.
immibis

23
@immibis O título do desafio foi escolhido com sabedoria. Este é Moby Dick, aproximadamente . :-)
Arnauld

3
@ Nathaniel Houve muitas atualizações, de modo que dificilmente seria legível e não seria realmente informativo. Em vez disso, adicionei um log de alterações com breves explicações sobre as melhorias.
Arnauld

45
Eu acho que seu programa está realmente fazendo uma tradução perfeita para o gaélico.
Beska

1
@ Draco18s É difícil dizer se essa vírgula foi um palpite bom ou ruim. Se foi um palpite ruim, a função de previsão pode ter legitimamente tentado escrever uma carta depois de qualquer outra letra que estava realmente lá, em vez da vírgula, uma vez recebida.
Arnauld

91

Perl, 2 · 70525 + 326508 = 467558

Preditor

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

Para executar este programa, você precisa deste arquivo aqui , que deve ser nomeado B. (Você pode alterar esse nome de arquivo na segunda instância do caractere Bacima.) Veja abaixo como gerar esse arquivo.

O programa usa uma combinação de modelos de Markov essencialmente como nesta resposta do usuário 2699 , mas com algumas pequenas modificações. Isso produz uma distribuição para o próximo caractere. Usamos a teoria da informação para decidir se devemos aceitar um erro ou gastar bits de armazenamento em Bdicas de codificação (e, se sim, como). Utilizamos a codificação aritmética para otimizar os bits fracionários do modelo.

O programa tem 582 bytes (incluindo uma nova linha final desnecessária) e o arquivo binário Btem 69942 bytes; portanto, de acordo com as regras de pontuação de vários arquivos , obtemos L582 + 69942 + 1 = 70525.

O programa quase certamente requer uma arquitetura de 64 bits (little-endian?). Demora aproximadamente 2,5 minutos para executar em uma m5.largeinstância no Amazon EC2.

Código de teste

# 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

O equipamento de teste assume que o envio está no arquivo submission.pl, mas isso pode ser facilmente alterado na segunda linha.

Comparação de texto

"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

Este exemplo (escolhido em outra resposta ) ocorre bastante tarde no texto, portanto o modelo é bastante desenvolvido nesse ponto. Lembre-se de que o modelo é aumentado por 70 kilobytes de "dicas" que ajudam diretamente a adivinhar os personagens; não é direcionado simplesmente pelo pequeno trecho de código acima.

Gerando dicas

O programa a seguir aceita o código exato de envio acima (na entrada padrão) e gera o Barquivo exato acima (na saída padrão):

@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],'}}'

Demora aproximadamente o tempo necessário para executar o envio, pois ele executa cálculos semelhantes.

Explicação

Nesta seção, tentaremos descrever o que essa solução faz com detalhes suficientes para que você possa "experimentá-la em casa". A principal técnica que diferencia essa resposta das outras é algumas seções abaixo, como o mecanismo de "rebobinar", mas antes de chegarmos lá, precisamos configurar o básico.

Modelo

O ingrediente básico da solução é um modelo de linguagem. Para nossos propósitos, um modelo é algo que pega uma certa quantidade de texto em inglês e retorna uma distribuição de probabilidade no próximo caractere. Quando usamos o modelo, o texto em inglês será um prefixo (correto) de Moby Dick. Observe que a saída desejada é uma distribuição e não apenas um palpite para o caracter mais provável.

No nosso caso, usamos essencialmente o modelo nesta resposta do usuário2699 . Não usamos o modelo da resposta de maior pontuação (exceto a nossa) de Anders Kaseorg justamente porque não conseguimos extrair uma distribuição em vez de apenas um palpite. Em teoria, essa resposta calcula uma média geométrica ponderada, mas obtivemos resultados um pouco ruins quando a interpretamos literalmente. "Roubamos" um modelo de outra resposta, porque nosso "molho secreto" não é o modelo, mas a abordagem geral. Se alguém tiver um modelo "melhor", deverá conseguir melhores resultados usando o restante de nossas técnicas.

Como observação, a maioria dos métodos de compactação, como o Lempel-Ziv, pode ser vista como um "modelo de linguagem" dessa maneira, embora seja necessário apertar os olhos um pouco. (É particularmente complicado para algo que transforma uma Burrows-Wheeler!) Além disso, observe que o modelo por user2699 é uma modificação de um modelo de Markov; essencialmente, nada mais é competitivo para esse desafio ou talvez até modelar texto em geral.

Arquitetura geral

Para fins de entendimento, é bom dividir a arquitetura geral em várias partes. Da perspectiva de mais alto nível, é necessário haver um pouco de código de gerenciamento de estado. Isso não é particularmente interessante, mas, para ser completo, queremos enfatizar que, em todo momento, o programa é solicitado a adivinhar o seguinte, ele tem disponível um prefixo correto de Moby Dick. Nós não usamos nossas suposições incorretas do passado de forma alguma. Por uma questão de eficiência, o modelo de linguagem provavelmente pode reutilizar seu estado dos primeiros N caracteres para calcular seu estado para os primeiros (N + 1) caracteres, mas, em princípio, ele pode recompilar as coisas do zero toda vez que é invocado.

Vamos deixar esse "driver" básico de lado e espiar dentro da parte que adivinha o próximo caractere. Conceitualmente, ajuda a separar três partes: o modelo de linguagem (discutido acima), um arquivo de "dicas" e um "intérprete". A cada passo, o intérprete solicitará ao modelo de linguagem uma distribuição para o próximo caractere e possivelmente lerá algumas informações do arquivo de dicas. Em seguida, combinará essas partes em um palpite. Precisamente, quais informações estão no arquivo de dicas e como são usadas serão explicadas mais tarde, mas, por enquanto, ajuda a manter essas partes separadas mentalmente. Observe que, em termos de implementação, o arquivo de dicas é literalmente um arquivo (binário) separado, mas poderia ter sido uma string ou algo armazenado dentro do programa. Como uma aproximação,

Se alguém estiver usando um método de compactação padrão, como bzip2, como nesta resposta , o arquivo "dicas" corresponderá ao arquivo compactado. O "intérprete" corresponde ao descompressor, enquanto o "modelo de linguagem" é um pouco implícito (como mencionado acima).

Por que usar um arquivo de dica?

Vamos escolher um exemplo simples para analisar melhor. Suponha que o texto tenha Ncaracteres longos e bem aproximados por um modelo em que cada caractere seja (independentemente) a letra Ecom probabilidade ligeiramente menor que a metade, Tsimilarmente com probabilidade ligeiramente menor que a metade e Acom probabilidade 1/1000 = 0,1%. Vamos supor que nenhum outro personagem seja possível; de qualquer forma, Aé bem parecido com o de um personagem anteriormente invisível do nada.

Se operamos no regime L0 (como a maioria, mas não todas, das outras respostas a essa pergunta), não há melhor estratégia para o intérprete do que escolher uma das opções Ee T. Em média, ele receberá cerca da metade dos caracteres corretos. Então E ≈ N / 2 e a pontuação ≈ N / 2 também. No entanto, se usarmos uma estratégia de compactação, podemos compactar para um pouco mais de um bit por caractere. Como L é contado em bytes, obtemos L ≈ N / 8 e, portanto, obtemos ≈ N / 4, duas vezes melhor que a estratégia anterior.

Atingir essa taxa de pouco mais de um bit por caractere para este modelo não é trivial, mas um método é a codificação aritmética.

Codificação aritmética

Como é comumente conhecido, uma codificação é uma maneira de representar alguns dados usando bits / bytes. Por exemplo, ASCII é uma codificação de 7 bits / caracteres do texto em inglês e caracteres relacionados, e é a codificação do arquivo Moby Dick original em consideração. Se algumas letras são mais comuns que outras, uma codificação de largura fixa como ASCII não é ideal. Em tal situação, muitas pessoas buscam a codificação de Huffman . Isso é ideal se você deseja um código fixo (sem prefixo) com um número inteiro de bits por caractere.

No entanto, a codificação aritmética é ainda melhor. Grosso modo, é capaz de usar bits "fracionários" para codificar informações. Existem muitos guias para codificação aritmética disponíveis online. Iremos pular os detalhes aqui (especialmente a implementação prática, que pode ser um pouco complicada do ponto de vista da programação) por causa dos outros recursos disponíveis online, mas se alguém reclamar, talvez esta seção possa ser mais aprofundada.

Se houver texto realmente gerado por um modelo de linguagem conhecido, a codificação aritmética fornecerá uma codificação essencialmente ótima do texto desse modelo. Em certo sentido, isso "resolve" o problema de compactação desse modelo. (Portanto, na prática, a questão principal é que o modelo não é conhecido e alguns são melhores que outros na modelagem de texto humano.) Se não foi permitido cometer erros neste concurso, então no idioma da seção anterior , uma maneira de produzir uma solução para esse desafio seria usar um codificador aritmético para gerar um arquivo "dicas" a partir do modelo de linguagem e, em seguida, usar um decodificador aritmético como "intérprete".

Nesta codificação essencialmente ideal, acabamos gastando -log_2 (p) bits para um caractere com probabilidade p, e a taxa de bits geral da codificação é a entropia de Shannon . Isso significa que um caractere com probabilidade próxima a 1/2 leva cerca de um bit para codificar, enquanto um caractere com probabilidade 1/1000 leva cerca de 10 bits (porque 2 ^ 10 é aproximadamente 1000).

Mas a métrica de pontuação para esse desafio foi bem escolhida para evitar a compactação como a estratégia ideal. Teremos que descobrir uma maneira de cometer alguns erros como compensação por obter um arquivo de dicas mais curto. Por exemplo, uma estratégia que se pode tentar é uma estratégia simples de ramificação: geralmente tentamos usar a codificação aritmética quando possível, mas se a distribuição de probabilidade do modelo é "ruim", de alguma forma, apenas adivinhamos o personagem mais provável e não ' tente codificá-lo.

Por que cometer erros?

Vamos analisar o exemplo anterior para motivar por que podemos querer cometer erros "intencionalmente". Se usarmos a codificação aritmética para codificar o caractere correto, gastaremos aproximadamente um bit no caso de um Eou T, mas cerca de dez bits no caso de um A.

No geral, essa é uma codificação muito boa, gastando um pouco mais de um pouco por caractere, embora haja três possibilidades; basicamente, Aé bastante improvável e não acabamos gastando seus dez bits correspondentes com muita frequência. No entanto, não seria bom se pudéssemos cometer um erro no caso de um A? Afinal, a métrica para o problema considera 1 byte = 8 bits de comprimento como equivalente a 2 erros; portanto, parece que se deve preferir um erro ao invés de gastar mais de 8/2 = 4 bits em um caractere. Gastar mais de um byte para salvar um erro definitivamente parece subótimo!

O mecanismo de "rebobinar"

Esta seção descreve o principal aspecto inteligente desta solução, que é uma maneira de lidar com suposições incorretas sem nenhum custo.

Para o exemplo simples que analisamos, o mecanismo de rebobinagem é particularmente direto. O intérprete lê um bit do arquivo de dicas. Se é um 0, adivinha E. Se for um 1, adivinha T. Na próxima vez que for chamado, ele verá qual é o caractere correto. Se o arquivo de dica estiver bem configurado, podemos garantir que, no caso de um Eou T, o intérprete adivinhe corretamente. Mas que tal A? A ideia do mecanismo de retrocesso é simplesmente não codificar Aem tudo . Mais precisamente, se o intérprete mais tarde descobrir que o caractere correto era um A, metaforicamente " rebobina a fita": retorna o bit lido anteriormente. A parte que ele lê pretende codificar EouT, mas agora não; será usado mais tarde. Neste exemplo simples, isso basicamente significa que continua adivinhando o mesmo caractere ( Eou T) até acertar; depois lê outro pedaço e continua.

A codificação para esse arquivo de dicas é muito simples: transforme todos os Es em 0 bits Tes em 1 bits, ignorando-os Acompletamente. Pela análise no final da seção anterior, esse esquema comete alguns erros, mas reduz a pontuação geral ao não codificar nenhum dos As. Como efeito menor, ele também economiza o tamanho do arquivo de dicas, porque acabamos usando exatamente um bit para cada um Ee T, em vez de um pouco mais do que um pouco.

Um pouco de teorema

Como decidimos quando cometer um erro? Suponha que nosso modelo nos dê uma distribuição de probabilidade P para o próximo caractere. Separaremos os caracteres possíveis em duas classes: codificado e não codificado . Se o caractere correto não estiver codificado, usaremos o mecanismo "rebobinar" para aceitar um erro sem nenhum custo. Se o caractere correto for codificado, usaremos outra distribuição Q para codificá-lo usando codificação aritmética.

Mas qual distribuição Q devemos escolher? Não é difícil ver que todos os caracteres codificados devem ter maior probabilidade (em P) do que os caracteres não codificados. Além disso, a distribuição Q deve incluir apenas os caracteres codificados; afinal, não estamos codificando os outros, por isso não devemos "gastar" entropia neles. É um pouco mais complicado ver que a distribuição de probabilidade Q deve ser proporcional a P nos caracteres codificados. Reunir essas observações significa que devemos codificar os caracteres mais prováveis, mas possivelmente não os menos prováveis, e que Q é simplesmente P redimensionado nos caracteres codificados.

Além disso, verifica-se que existe um teorema interessante sobre qual "corte" deve-se escolher para os caracteres de codificação: você deve codificar um caractere contanto que seja pelo menos 1 / 5.393 tão provável quanto os outros caracteres codificados combinados. Isso "explica" a aparência da constante aparentemente aleatória 5.393mais próxima do final do programa acima. O número 1 / 5.393 ≈ 0,18542 é a solução para a equação -p log (16) - p log p + (1 + p) log (1 + p) = 0 .

Talvez seja uma idéia razoável escrever esse procedimento no código. Este fragmento está em 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;
}

Juntando tudo

Infelizmente, a seção anterior é um pouco técnica, mas se juntarmos todas as outras peças, a estrutura será a seguinte. Sempre que o programa é solicitado a prever o próximo caractere após um determinado caractere correto:

  1. Adicione o caractere correto ao prefixo correto conhecido de Moby Dick.
  2. Atualize o modelo (Markov) do texto.
  3. O segredo : se o palpite anterior estiver incorreto, rebobine o estado do decodificador aritmético para o estado anterior ao palpite anterior!
  4. Peça ao modelo de Markov para prever uma distribuição de probabilidade P para o próximo caractere.
  5. Transforme P em Q usando a sub-rotina da seção anterior.
  6. Peça ao decodificador aritmético que decodifique um caractere do restante do arquivo de dicas, de acordo com a distribuição Q.
  7. Adivinha o personagem resultante.

A codificação do arquivo de dicas funciona da mesma forma. Nesse caso, o programa sabe qual é o próximo caractere correto. Se é um caractere que deve ser codificado, é claro que se deve usar o codificador aritmético; mas se for um caractere não codificado, ele simplesmente não atualizará o estado do codificador aritmético.

Se você entende o contexto teórico da informação como distribuições de probabilidade, entropia, compressão e codificação aritmética, mas tentou e não conseguiu entender este post (exceto por que o teorema é verdadeiro), informe-nos e podemos tentar esclarecer as coisas. Obrigado pela leitura!


8
Uau, resposta impressionante. Presumo que seja necessário código adicional para gerar o Barquivo? Em caso afirmativo, você pode incluir isso em sua resposta?
1913 Nathaniel

8
Excelente! A primeira (e até agora a única) resposta para quebrar a barreira da pontuação de 500k.
precisa saber é o seguinte

5
"tersed a baleia shite" omg eu estou chorando
Phill

5
Como nenhuma resposta nova foi publicada durante o período de recompensa, concedo-a à sua resposta, como a melhor pontuação e a abordagem mais sofisticada. Se você tiver tempo, eu realmente apreciaria uma explicação mais profunda de como essa resposta funciona, ou seja, qual é exatamente o algoritmo?
Nathaniel

2
@ Nathaniel: eu adicionei uma explicação para este post. Deixe-me saber se você acha que é detalhado o suficiente para reproduzir a solução.
A. Rex

77

Python 3, 2 · 267 + 510193 = 510727

Preditor

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]

Utiliza uma combinação bayesiana ponderada dos modelos Markov da ordem 0,…, 16, com pesos [1, 6, 12, 30, 65, 99, 87, 117, 118, 89, 95, 118, 96, 184, 126, 219, 126].

O resultado não é muito sensível à seleção desses pesos, mas eu os otimizei porque pude, usando o mesmo algoritmo de escalada de aceitação tardia que usei na minha resposta para “Reunir a maioria do Senado” , onde cada mutação candidata é apenas um incremento de ± 1 para um único peso.

Código de teste

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
A ferramenta certa para o trabalho. Ótima pontuação. Agradável.
agtoever

1
Possível esclarecimento: b"\0\3\6\r\34'&-20'\22!P\n[\26"é a representação ascii dos pesos, onde pequenos valores não imprimíveis são escapados em octal.
Cœur

Atualizei a pergunta com uma versão do arquivo em que o texto não está quebrado - você pode tentar reexecutar seu código (isso pode ser um pouco melhor) #
Nathaniel Nathaniel

3
Obrigado por essa explicação - se você pudesse editar uma sinopse para a pergunta, seria ótimo. (A experiência com meu desafio anterior, Paint Starry Night, é que esses procedimentos de otimização são a parte mais interessante das respostas, por isso é muito melhor que as respostas incluam o código usado para fazer isso e uma explicação. Incluí uma regra nos dois desafios dizendo que deveriam.)
Nathaniel

1
@Christoph Na verdade, minha combinação de modelos é uma média geométrica ponderada. Mas a média do PAQ no domínio logístico é um pouco diferente - vou ter que ver se é melhor.
Anders Kaseorg 18/01/19

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

Experimente online!

Uma função usando variáveis ​​globais. Aprende à medida que constrói um modelo no nível da palavra: dado o que é visto até agora nesta palavra , qual é o próximo personagem mais comum? À medida que mais informações entram, ele aprende muito bem as palavras comuns do texto e também aprende o caractere mais comum para iniciar a próxima palavra.

Por exemplo:

  • Se o que é visto até agora é 'Captai', ele prevê um "n"
  • Se for "Capitão", ele prevê um espaço
  • Se for o início de uma palavra e a última palavra for "Capitão", ele prevê um 'A'
  • Se a palavra até agora é 'A', ela prediz um 'h' (e depois 'a' e 'b'; da mesma forma para 'C').

Não se sai muito bem no início, mas no final existem grandes partes de palavras reais saindo. A opção de fallback é um espaço e, após um único espaço, é um "a", a menos que a letra anterior seja de "nedtfo", um dígito ou um hífen ou apóstrofo. Ele também prevê agressivamente quebras de linha após 71 caracteres ou se um espaço era esperado após 66. Ambos foram sintonizados com os dados ("t" é muito mais comum após um espaço, mas já foi previsto com mais frequência, portanto " um "é um palpite melhor fora desses seis casos especiais).

Aprender quais pares de palavras foram combinados e prever o mapeamento acabou não valendo a pena.


Termina com um texto como este:

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!

que corresponde a esta parte da entrada:

ao seu redor.

"Eu o vi quase no mesmo instante, senhor, que o capitão Ahab viu, e eu gritei", disse Tashtego.

"Não é o mesmo instante; não é o mesmo - não, o dobrão é meu, o destino reservou o dobrão para mim. Eu só; nenhum de vocês poderia ter criado a baleia branca primeiro. Lá ela sopra! - aí ela sopra! - -tá ela sopra!

Você pode ver onde os nomes próprios em particular saem muito bem, mas o final das palavras também está correto. Quando é visto "dou", espera "dúvida", mas quando o "l" aparece, ele fica "dobrado".

Se você executá-lo pela segunda vez com o mesmo modelo que ele acabou de criar, obtém imediatamente outros 92k de correção (51,7% -> 59,3%), mas sempre fica abaixo de 60% a partir da segunda iteração.


O código de medição está no link TIO, ou aqui está uma versão um pouco melhor:

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 tem a saída estimada no final.


3
Esta é uma excelente abordagem!
Skyler

2
muito <s> </s>;)
FantaC 14/01

1
+1 porque essa abordagem me lembrou o algoritmo de compactação LZW.
Marcos

25

C ++, pontuação: 2 * 132 + 865821 = 866085

Obrigado a @Quentin por salvar 217 bytes!

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

Uma solução muito simples que, dada um caractere, apenas gera o caractere que aparece com mais frequência após o caractere de entrada.

Verifique a pontuação com:

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

Editar: Usar whale2.txtfornece uma pontuação melhor.


5
Você pode traduzir essa matriz em uma string literal e inline diretamente no lugar de Lsalvar um monte de personagens :)
Quentin

@ Quentin Thanks! Agora estou me perguntando por que não pensei nisso em primeiro lugar ...
Steadybox 10/01/18

20

Python, 2 * 516 + 521122 = 522154

Algoritmo:

Ainda outra submissão em python, esse algoritmo calcula a próxima letra mais provável, observando seqüências de comprimento 1, ..., l. A soma das probabilidades é usada e existem alguns truques para obter melhores resultados.

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

Resultados:

Principalmente sem sentido, embora você possa ver que ele pega uma frase ocasional, como "Pai 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

Código do teste:

Muito simples, gera alguns exemplos do texto em diferentes pontos. Usa whale2.txt, pois isso evita uma lógica extra para calcular novas linhas.

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
Bem vindo ao site! Esta é uma fantástica primeira submissão. :)
DJMcMayhem

@DJMcMayhem, obrigado pela recepção. Eu gosto de assistir por um tempo agora, este é o primeiro concurso a chamar minha atenção para uma inscrição.
user2699

19

C (gcc) , 679787 652892

84 76 bytes, 679619 652740 suposições incorretas

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

Experimente online!

Atualização: ~ 27000 pontos com o arquivo atualizado, 16 pontos (8 bytes) com uma função com melhor desempenho.

Explicação

A maneira como isso funciona é que, à medida que o código percorre o texto, ele memoriza o último caractere que encerrou qualquer sequência de 4 caracteres e retorna esse valor. Um pouco semelhante à abordagem de Arnauld acima, mas se baseia na probabilidade inerente de duas seqüências de 4 caracteres que terminam da mesma maneira.

De-golfe:

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

... O link do TIO é inútil. Então a função retorna o valor da última atribuição?
usar o seguinte comando

Deixe-me editar a resposta com uma explicação, então :)

1
@Rogem Adicionei uma versão sem golfe (o que fiz porque também não consegui segui-la) - espero que isso não o incomode, mas reverta, se desejar.
Adam Davis

@AdamDavis na maioria das implementações de C, todas as variáveis ​​globais começam em zero. É um comportamento indefinido, por isso é usado apenas no código-golfe.
NieDzejkob

1
@NieDzejkob Ah, você está certo, obrigado! "O ANSI-C requer que todas as variáveis ​​estáticas / globais não inicializadas tenham que ser inicializadas com 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

seguido pelo bzip2-compressed whale2.txt com o primeiro byte ausente

Ignora sua entrada; gera a resposta correta. Isso fornece uma linha de base em uma extremidade; daniero fornece uma linha de base na outra extremidade.

Script do construtor:

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

Arnês de teste de E / S (tcc; corte da primeira linha para gcc). Esse equipamento de teste pode ser usado por qualquer pessoa em uma plataforma adequada que envie um programa completo que espere E / S de leitura / gravação. Ele usa E / S de byte por vez para evitar trapaças. O programa filho deve liberar a saída após cada byte para evitar o bloqueio.

#!/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
Acho que ele está perguntando: como isso não viola a 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.cláusula?

8
@Rogem Os dados compactados são colocados depois do que é mostrado aqui, e o código acessa a si próprio.
usar o seguinte comando

4
A pergunta diz: "Sua submissão será um programa ou função (etc.) que será chamado ou invocado várias vezes. Quando for chamado durante o nthtempo, receberá o enésimo caractere de whale.txtou whale2.txte deve apresentar seu palpite para o (n+1)thpersonagem." - Como esse requisito é cumprido? O código exibe o texto inteiro de whale.txtcada vez que é executado.
axiac 11/01

1
@axiac "está tudo bem, desde que o seu programa sempre dê um byte de saída antes de receber o próximo byte de entrada".
precisa saber é o seguinte

5
@axiac, dado o equipamento de teste, fico feliz em considerar o envio do programa a um byte do STDIN como "ligando ou invocando". O importante é que o programa retorne um byte de saída após cada byte de entrada, o que este realmente faz, quando executado através do equipamento de teste. Como a pergunta diz, "tudo está bem, desde que o seu programa sempre dê um byte de saída antes de receber o próximo byte de entrada".
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])

Experimente online!


... A ///resposta que imprime um espaço recebe 10 votos positivos, enquanto meu código só pode receber 3 ...

Explicação:

Para cada personagem, o programa:

  • aumentar frequency[prev][char]
  • Encontre o personagem que aparece mais vezes em frequency[char]
  • e produzi-lo.

  • Código ungolfed no link TIO, comentou.
  • O código é 131 bytes.
  • O código executado nos meus relatórios de máquina:
879504 / 1215235
Time: 62.01348257784468

que têm a pontuação total

2*131 + 879504 = 879766

Como não há como fazer upload de um arquivo grande para o TIO (exceto pedir a Dennis), o exemplo executado no link TIO executa o programa apenas para uma pequena parte do texto.

Em comparação com a resposta mais antiga, essa possui 362 caracteres mais incorretos, mas o código é mais curto em 255 bytes. O multiplicador faz com que minha inscrição tenha uma pontuação menor.


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

Essa abordagem usa uma tabela de consulta para aprender o caractere mais comum que segue uma determinada sequência. As teclas da tabela de pesquisa têm no máximo 4 caracteres; portanto, a função atualiza primeiro a tabela de pesquisa com o caractere atual e depois apenas verifica qual caractere é o mais provável de ocorrer após os 4 anteriores, incluindo o atual . Se esses 4 caracteres não forem encontrados na tabela de pesquisa, ele imprimirá um espaço.

Esta versão usa o whale2.txtarquivo, pois melhora bastante o número de suposições bem-sucedidas.

A seguir está o código usado para testar a 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();
    }
}

O código é executado em apenas 2 segundos. Apenas para constar, é isso que recebo quando modifico o tamanho das chaves da tabela de consulta (inclui os resultados de uma segunda execução sem redefinir o modelo):

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

Seria interessante saber por que um tamanho de chave de 4 caracteres é a melhor escolha nesse algoritmo.

Comparação de texto

Original:

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

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

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

Recriado:

"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 

Palpites:

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

Log de alterações

  • 569279 - alterado para whale2.txte, portanto, removido a otimização.
  • 577366 - otimizado com código que tentou adivinhar quando retornar um avanço de linha.
  • 590354 - versão original.

4
Obrigado por mostrar a variação conforme você altera o tamanho da chave e o limite da coluna!
Jeremy Weirich

Atualizei a pergunta com uma versão do arquivo em que o texto não está agrupado - você provavelmente pode salvar alguns pontos usando isso #
Nathaniel Nathaniel

@ Nathaniel faz de fato. Eu atualizei a resposta.
Charlie

Você pode salvar alguns bytes usando var em vez de declarar os tipos.
Ed T

1
À medida que o tamanho da chave aumenta, o número de acertos versus erros diminui e, portanto, mais espaços serão gerados quando uma tecla mais curta pode ter adivinhado o caractere correto. À medida que o tamanho da chave diminui, as estimativas individuais são menos precisas para os segmentos correspondentes. Eu suspeito que é por isso que um comprimento de quatro é ideal. Se você mantinha chaves de comprimentos múltiplos e usava correspondências mais curtas quando outras mais compridas não estavam disponíveis, espero que a taxa de acertos (e, portanto, a pontuação) melhore bastante em comprimentos de chaves mais longos.
Jeffrey L Whitledge

11

Java 7, caracteres de 1995, (1995 * 2 + 525158) 529148

Java é péssimo para pequenos tamanhos de programa. Enfim, tentei várias abordagens extremamente complexas e complicadas que produziram resultados surpreendentemente ruins. Posteriormente, voltei e fiz uma abordagem simples, que resultou em um tamanho menor de programa e melhores resultados.

Essa abordagem é realmente extremamente simples. Alimenta cegamente os x caracteres anteriores (além de todas as substrings desses caracteres) em uma hashtable, mapeada para o caractere atual. Em seguida, ele acompanha quais padrões preveem com mais precisão o caractere atual. Se padrões que precedem certos caracteres forem encontrados várias vezes, eles conseguirão prever o personagem. Dá precedência a seqüências mais longas e dá precedência a qualquer caractere que mais frequentemente segue uma determinada sequência. Esse algoritmo não sabe nada sobre o tipo de documento ou o idioma inglês.

Decidi usar 9 caracteres e tentar combinar palavras inteiras nos 9 caracteres anteriores, quando possível. Quando você não tenta fazer a correspondência de palavras nas cadeias, o tamanho ideal é de 6 caracteres, produzindo milhares de outras previsões erradas.

Uma observação interessante foi que o uso de 20 caracteres resultou em previsões ruins na primeira vez, mas com precisão de 99,9% nas passagens subsequentes. O algoritmo foi basicamente capaz de memorizar o livro em pedaços de 20 bytes sobrepostos, e isso foi distinto o suficiente para permitir que ele lembrasse o livro inteiro de um caractere por vez.

  • (1950 * 2 + 532919) 536819
  • (2406 * 2 + 526233) 531045 verificando pontuação para fazer melhores suposições
  • (1995 * 2 + 525158) 529148 mais ajustes, jogou fora alguns palavrões

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

Essa é uma pontuação muito boa para uma linguagem tão detalhada.
DJMcMayhem

1
Achei que valia a pena tentar, já que o tamanho do arquivo oferece muito espaço para melhorias em comparação com o tamanho do programa.
11138 Jim W

3
Isso não é compilável no Java 7 (ou em qualquer versão do Java, pelo que vale a pena). Você poderia corrigir seu código? Feito isso, terei prazer em jogar golfe para que sua pontuação melhore.
Olivier Grégoire

Não testado, mas esse deve ser exatamente o mesmo código, com um pouco de golfe: 950 bytes . Seu código atual continha alguns erros, portanto, não tenho certeza se preenchi tudo corretamente. Novamente, não testado, então basta comparar as versões para ver o que eu mudei / renomeei e veja se tudo ainda funciona da mesma forma que o código original. Definitivamente pode ser jogado um pouco mais, no entanto.
Kevin Cruijssen

Merda, eu fiz isso enquanto estava entediado com o meu antigo emprego e não levava o código comigo. Vou ter que dar uma olhada para ver onde está o erro de digitação.
21718 Jim Jim

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

Tentei isso de forma independente, mas acabei com o que é efetivamente uma versão inferior da resposta de Michael Homer. Espero que isso não torne minha resposta completamente obsoleta.

Isso cria ao longo do tempo um dicionário de palavras (definido de forma grosseira como cadeias terminadas por ou \n, diferenciando maiúsculas de minúsculas e incluindo pontuação). Em seguida, ele pesquisa neste dicionário por palavras que começam com o que até agora conhece a palavra atual, classifica a lista resultante por frequência de ocorrência (lentamente) e supõe que o próximo caractere seja o próximo na palavra correspondente mais comum. Se já tivermos a palavra correspondente mais comum ou se não existir mais, ela retornará .

Ele também cria um dicionário repugnantemente ineficiente de pares de palavras. Ao atingir um limite de palavra, ele adivinha que o próximo caractere é a primeira letra da segunda palavra no par de palavras correspondente mais comum ou tse não há correspondência. Não é muito inteligente, no entanto. A seguir Moby, o programa adivinha corretamente que o próximo personagem é D, mas depois esquece tudo sobre o contexto e geralmente acaba chamando a baleia de "Moby Duck" (porque a palavra "holandês" parece ser mais frequente na primeira metade do texto ) Seria fácil corrigir isso priorizando pares de palavras em detrimento de palavras individuais, mas espero que o ganho seja marginal (já que geralmente é correto a partir do terceiro caractere, e os pares de palavras não são tão úteis em primeiro lugar).

Eu poderia ajustar isso para corresponder melhor ao texto fornecido, mas não acho que o ajuste manual do algoritmo com base no conhecimento prévio da entrada esteja realmente no espírito do jogo, exceto por escolher t como o caractere de fallback após um espaço ( e eu provavelmente não deveria ter feito isso também), evitei isso. Eu ignorei o comprimento da linha conhecida do arquivo de entrada e, em vez disso, inseri-lo \napós cada 13 espaços - é quase certamente uma correspondência muito ruim, a principal intenção era manter o comprimento da linha razoável, em vez de corresponder à entrada.

O código não é exatamente rápido (~ 2 horas na minha máquina), mas no geral obtém cerca da metade dos caracteres corretos (49%). Espero que a pontuação seja marginalmente melhor se for executada whale2.txt, mas ainda não o fiz.

O início da saída é assim:

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

mas no final, parece um pouco mais com ... alguma coisa. Minha passagem favorita do final do livro,

e como nenhum deles pode ser meu, deixe-me então me despedaçar, enquanto ainda te persegue, ainda que amarrado a ti, maldita baleia! ASSIM, desisto da lança! "

sai como

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 "

Isso teria tornado A Ira de Khan muito mais confusa. E "solitário" → "formigamento" é uma substituição particularmente satisfatória.

Editar: salvou um byte excluindo um espaço estranho

Pontuação

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

Isso executa o programa para o texto de Moby Dick e gera o texto "previsto" para stdout e abusa do stderr para escrever a pontuação. Eu recomendo redirecionar a saída para um arquivo.


2
Bem-vindo ao PPCG!
Martin Ender

1
Não lambda i:i[1]seria mais barato do que lidar operator?
Draconis

@ Draconis Quase certamente.
georgewatson

9

C ++, 2 · 62829 + 318786 = 444444

Para executar este programa, você precisa deste arquivo aqui , que deve ser nomeado C.

O programa usa a mesma combinação de modelos de Markov que em nossa resposta anterior . Como antes, essa combinação é essencialmente o modelo desta resposta do usuário2699 , mas com algumas pequenas modificações.

Visto que essa resposta usa exatamente o mesmo modelo de antes, a melhoria é um mecanismo teórico da informação melhor do que o mecanismo de "rebobinar" descrito anteriormente. Isso permite cometer menos erros e também ter um comprimento combinado menor. O programa em si não é muito jogado, porque não é o principal colaborador da pontuação.

O programa tem 2167 bytes de comprimento (incluindo todas as guias para indentação e muitos outros caracteres desnecessários, mas antes do código de teste) e o arquivo binário Ctem 60661 bytes, portanto, de acordo com as regras de pontuação de vários arquivos , obtemos L2167 + 60661 + 1 = 62829.

O programa leva aproximadamente 8 minutos para ser executado em uma m5.4xlargeinstância no Amazon EC2 e usa um pouco mais de 16 GB de memória. (Esse uso excessivo de memória não é necessário - nós também não otimizamos isso.)

#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 bytes, erros 526092 (usando whale2.txt). Definitivamente, isso é capaz de melhorar ainda mais, mas chegou ao estágio "bom o suficiente para publicar".

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

A idéia é armazenar as frequências de todas as execuções de 2, 3, 4, ..., 10 caracteres. Para cada um desses comprimentos L, verificamos se os caracteres L-1 mais recentes correspondem a um padrão armazenado; Nesse caso, nosso palpite g L é o próximo caractere mais frequente após esse padrão. Coletamos até nove palpites dessa maneira. Para decidir qual palpite usar, ponderamos a frequência de cada padrão pelo seu comprimento até a 8ª potência. O palpite com a maior soma de frequências ponderadas é escolhido. Se não houver padrões que correspondam, adivinhamos espaço.

(O comprimento máximo do padrão e o expoente de ponderação foram escolhidos por tentativa e erro para fornecer o menor número possível de suposições incorretas.)

Aqui está minha versão de trabalho em andamento não destruída:

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 o chicote de teste:

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

Aqui está um exemplo de saída do início do texto. Já começamos a ver a capacidade de terminar palavras depois de ver sua primeira carta ( in, to, and, by, também, aparentemente, 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 

Perto do final, ainda existem muitos erros, mas também muitas seqüências muito boas ( shmage seashawkspor exemplo).

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 observar alguns dos erros e adivinhar qual palavra o algoritmo "esperava". Por exemplo, depois sail, o programa ambas as vezes prediz o--para sailor, presumo. Ou então, depois que , aele espera - npossivelmente por causa da ocorrência comum de , and.


Changelog:

  • 274 * 2 + 526092 = 526640 Jogou o algoritmo, com o custo de alguns erros extras
  • 306 * 2 + 526089 = 526701 Versão original

6

Python 2, pontuação: 2 * (407 + 56574) + 562262 = 676224

Procura por palavras que correspondam aos caracteres anteriores em uma lista de  todas as  palavras mais usadas no texto, classificadas pelo número de ocorrências.

Código:

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"

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

Suíte de teste:

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

Editar: Usar whale2.txtfornece uma pontuação melhor.


5

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

Mais um envio de prefixo-frequência. Corra whale2.txte obtenha pontuação semelhante (um pouco pior) do que outras.

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

Este avidamente encontra a corda mais longa que começa com um sufixo da história e, se houver vários candidatos, faça o tiebreak com cordas mais curtas.

Por exemplo: Se os últimos 7 personagens são abcdefgh, ea cadeia abcdefghie abcdefghjaparece com a maior frequência em todas as cordas da forma abcdefgh*, a saída será tanto iou j, tiebreak com sufixos mais curtos ( bcdefgh, cdefgh...).

Por razões desconhecidas, nada mais que 7 e meu computador não tem RAM suficiente para executá-lo. Mesmo com 7, preciso fechar todos os navegadores da web para executá-lo.


Código de teste:

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

Exemplo de saída:

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

Este está perto do final do texto. A maioria das palavras longas são previstos bastante precisão ( 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

Letras maiúsculas não parecem boas.


Trie parece ser mais do que eu esperava que consome memória ...
user202729

... e também mais difícil de implementar.
usar o seguinte comando

4

Python 2, 756837

Usa algo que pode ser cadeias de Markov?

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

1
Explicação rápida: zlib.decompress('...')Avalia {'G?':' ', 'G;':' ','G"':' ',.......}e aé um dicionário que mapeia de 2 para 1 caractere. Basicamente, variante de 2 caracteres da resposta do Steadybox .
usar o seguinte comando

1
Como posso ver, o literal é 17780 bytes. Você pode reduzi-lo para 11619 caracteres removendo espaços em branco no conteúdo descompactado, o que economiza 12322 bytes. (se contei corretamente) Além disso ... a conversão de códigos de escape hexadecimais em caracteres brutos reais pode economizar ainda mais bytes.
precisa saber é o seguinte

Como faço para postar algo aqui se for bytes brutos?
Skyler

1
xxd, hexdump, uuencode, Ou similar
Peter Taylor

@ user202729 Note que o código Python não pode conter bytes NUL brutos reais.
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)

Exemplo:

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

Usa três arquivos auxiliares pré-calculados:

  • seqwords contém as 236 palavras mais comuns.
  • wordsseq contém uma sequência comprimida por LZMA dessas palavras e, para todas as palavras que não estão entre as 236 mais comuns, o comprimento.
  • dicttriescontém, para cada comprimento de palavra, uma árvore de decisão que contém todas as palavras restantes. Nessas tentativas, as entradas são selecionadas à medida que avançamos.

Dessa forma, alcançamos uma taxa de erro significativamente menor do que todos os outros esquemas com perdas; infelizmente, o wordsseqarquivo ainda é grande demais para ser competitivo.

Aqui está uma versão completa que cria os arquivos e faz a análise:

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;

Tal como está, esta solução é WIP e, portanto, não destruída. Também considerando que o tamanho real do código mal tem impacto na pontuação, pensei em postar minha resposta antes de começar a otimizá-la.
(Código completo disponível aqui: https://github.com/BrainStone/MobyDickRNG - Inclui programa completo e pesquisa de sementes)

Esta solução é baseada em um RNG. Primeiro analiso o texto. Crio um mapa que conta as ocorrências de dois caracteres consecutivos. Então eu crio um mapa de distribuição. Tudo isso é feito estaticamente, portanto, deve estar de acordo com as regras.

Então, ao tentar imprimir o texto, faço uma pesquisa e seleciono um caracter aleatório dos possíveis. Embora isso geralmente produza resultados piores do que apenas a saída da carta seguinte mais comum, é provável que haja sementes divinas que produzam melhores resultados. É por isso que a semente é codificada. Atualmente, estou procurando a melhor semente. E atualizarei esta resposta assim que encontrar melhores sementes. Portanto, fique informado!

Se alguém quiser procurar sementes por conta própria ou usar RNGs diferentes, fique à vontade para fazer o repo.

Método usado para calcular a pontuação: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15

Observe que, embora a pontuação total seja a pior no momento, ela supera a contagem de erros de apenas espaços de saída. E as chances são boas de que a pontuação caia, verificando mais sementes.

Changelog

  • 24/01/2018 : Resposta inicial publicada.
    Sementes verificadas: 0-50000. Pontuação: 2305 * 2 + 1017754 = 1022364
  • 24/01/2018 : Jogou golfe mínimo. Adicionado link para o método de cálculo da pontuação.
    Sementes verificadas: 0-80000. Pontuação: 1920 * 2 + 1017754 = 1021594 (-770)
  • 02/02/2018 : Nova semente (10087702) (não encontrou tempo para corrigir a submissão)
    Sementes verificadas: 0-32000000. Pontuação: 1923 * 2 + 1017344 = 1021190 (-404)

Você poderia incluir um equipamento de teste em sua resposta que avaliasse a pontuação?
Nathaniel

@ Nathaniel Liguei o código de pontuação diretamente. Além do repositório, você consideraria isso o suficiente?
BrainStone 24/01

Ao revisar as regras, notei que violei algumas delas. Eu, naturalmente, irá atualizar a minha resposta quando eu corrigir os problemas
BrainStone

Você acabará codificando o texto na semente aleatória. Veja a linguagem de programação esotérica Seed , e você pode fazer engenharia reversa no programa MT19937 e vencer esta resposta (se puder).
precisa saber é o seguinte

Boa ideia, mas não ajudará a obter uma boa pontuação. +1 de qualquer maneira.
usar o seguinte comando

3

Ruby, 1164418 (ai)

Eu só queria ver o quão bem eu poderia fazer sem verificar outras respostas.
Não tenho certeza se isso é permitido, pois inclui um literal que eu gerei através da análise do arquivo, mas mesmo que não fosse, não era como se estivesse correndo o risco de bater em alguém.

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

Como eu gerei x

Primeiro, eu gerei a.txtcom o seguinte:

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

Então eu gerei a.csv:

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

Então eu o analisei xcom o seguinte 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

Como eu marquei

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

Tenho certeza que é permitido; se você analisou o arquivo, bom trabalho! Somente se o programa fizer isso é inválido.
Erik the Outgolfer

@EriktheOutgolfer> _> (silenciosamente desliza a "(não concorrentes)" no título)
NO_BOOT_DEVICE

Por quê? Se isso é válido, está competindo, mesmo que não bata muito. Se for inválido (ou seja, sua solução lê o arquivo e não contém apenas um literal), ele deve ser excluído.
Erik the Outgolfer

Hummm. Eu pensei que você queria dizer se algum programa analisava o arquivo, e não apenas a solução.
NO_BOOT_DEVICE

1
Não consigo ler Ruby, mas acho que isso é válido. Tendo o literal dentro do programa é completamente bom, não há problema algum.
Nathaniel #

2

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

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

Experimente online!

Tabela de frequências bastante simples. Cada posição na sequência corresponde ao código ascii do caractere atual (menos 10 = 0x0a = '\ n', o caractere mais baixo do arquivo) e o caractere em cada índice é o próximo caractere mais frequente. Supondo que calculei as frequências corretamente ...

Testado com o código do teste de user202729


Você pode salvar alguns bytes usando def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]?
Neil

0

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

Precisão perfeita em apenas 644449 bytes

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

O código completo não pode caber em uma resposta, então eu o coloquei aqui e substitui a grande cadeia de caracteres binária literal por b '###' no texto da resposta.

Isso é gerado com o código a seguir, onde "modified.py" é o arquivo gerado e "cheatsheet.txt" é o arquivo whale2.txt que inicia no segundo caractere.

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

O código pode ser executado adicionando o seguinte ao final de "modified.py". "whale2.txt" deve estar no mesmo diretório que "modified.py" e a saída será gravada em "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)

Esta resposta não acessa diretamente whale.txt ou whale2.txt. Ele utiliza bibliotecas de compactação padrão existentes, conforme explicitamente permitido nas regras.


pode haver um "\ r \ n" no qual eu não consegui me livrar do Windows quando os estava contando
Legorhin 26/09

2
Sim, isso foi um erro de digitação que se propagou
Legorhin 26/09
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.