Moby Dickを書きます


297

これは、ハーマンメルビルのMoby-Dickのテキストを含む1.2Mb ASCIIテキストファイルです。または、クジラ。あなたの仕事は、このファイルに一度に1文字ずつ与えられるプログラムまたは関数(またはクラスなど-以下を参照)を書くことであり、各ステップで次の文字を推測する必要があります。

これはです。あなたのスコアは

2*L + E

ここLで、送信のサイズはバイト単位で、E誤って推測した文字数です。最も低いスコアが勝ちます。

さらなる詳細

送信するのは、複数回呼び出されるか、呼び出されるか、データを送信するプログラムまたは関数(など)です。(正確には1215235回。)n 番目に呼び出されると、orのn 番目の文字が与えられ、(n + 1番目の文字に対する推測を出力する必要があります。スコアの構成要素は、誤って推測した文字の総数です。whale.txtwhale2.txtE

ほとんどの送信では、呼び出しと呼び出しの間に状態を保存する必要があります。これにより、呼び出し回数と以前の入力が何であったかを追跡できます。これを行うには、外部ファイルに書き込むstaticか、グローバル変数を使用するか、関数ではなくクラスを送信するか、状態モナドを使用するか、その他の言語で機能するものを使用します。送信には、最初の呼び出しの前に状態を初期化するために必要なコードを含める必要があります。

プログラムは決定的に実行する必要があるため、同じ入力が与えられると常に同じ推測が行われます(したがって、常に同じスコアが取得されます)。

回答には、提出物だけでなくE、スコアの一部を計算するために使用したコードも含める必要があります。これは提出物と同じ言語で書かれている必要はなく、そのバイト数にカウントされません。読みやすくすることをお勧めします。

提出物とこのスコア計算プログラムの間のインターフェースに関しては、プログラムが常に次の入力バイトを受け取る前に1バイトの出力を与える限り、何でも問題ありません。(たとえば、すべての入力を含む文字列を渡すだけで、すべての出力を含む文字列を取得することはできません。)

実際にテストプログラムを実行し、エントリを提出する前にスコアを計算/検証する必要があります。提出物のスコアが検証するには遅すぎる場合、原則としてそのスコアが何であるかを知っていても、競争する資格がありません。

Lスコアの構成要素は、コードゴルフチャレンジの通常のルールに従って計算されます。提出物に複数のファイルが含まれる場合は、その場合のスコアリングディレクトリ構造に関する規則に注意してください。コードで使用するデータはすべてLスコアに含める必要があります。

あなたは、既存のライブラリをインポートすることがありますが、他の外部ファイルをロードしないかもしれない、とあなたのコードがアクセスすることはできませんwhale.txtかをwhale2.txt上記以外の方法でファイルします。事前学習済みのニューラルネットワークやその他の統計データのソースをロードすることはできません。(ニューラルネットワークを使用しても構いませんが、提出物に体重データを含めて、バイトカウントにカウントする必要があります。)何らかの理由で、言語またはライブラリにMoby Dickのテキストの一部またはすべてを提供する機能が含まれている場合、その機能を使用することはできません。それ以外に、言語またはその標準ライブラリの一部である限り、テキスト処理、予測、または圧縮に関連する機能を含む、他の任意の組み込み機能またはライブラリ機能を使用できます。統計データのソースを含む、よりエキゾチックで特殊なルーチンの場合、それらを自分で実装し、バイトカウントに含める必要があります。

一部の提出物には、それ自体がコードによって生成されるコンポーネントが含まれる可能性があります。その場合は、それらを生成するために使用されたコードを回答に含めて、その仕組みを説明してください。(このコードが送信を実行するために必要でない限り、バイトカウントには含まれません。)

歴史的な理由から、ファイルには2つのバージョンがあり、いずれかを回答に使用できます。ではwhale2.txt改行は段落の最後にのみ表示されますので(上記のリンク)テキストは、ラップされていません。オリジナルでwhale.txtは、テキストは74文字の幅に折り返されているため、各行の終わりとテキストを予測する必要があります。これは挑戦をより厄介にしますのでwhale2.txt、新しい答えのために推奨されます。両方のファイルは同じサイズで、1215236バイトです。


要約すると、すべての回答には次の事項を含める必要があります。

  • あなたの提出自体。(コードと、それが使用するデータファイル-大きい場合、これらはリンクになります。)
  • コードの仕組みの説明。I / Oメソッドと、次の文字を予測する方法を説明してください。あなたのアルゴリズムの説明は重要であり、良い説明は私から賞金を得るでしょう。
  • スコアの評価に使用したコード。(これが以前の回答と同じ場合は、リンクするだけです。)
  • 提出物の生成に使用したコードとそのコードの説明。これには、パラメーターの最適化、データファイルの生成などに使用したコードが含まれます(これはバイトカウントにはカウントされませんが、回答に含める必要があります)。

リーダーボード

報奨金

随時、さまざまなアプローチを奨励するための報奨金を提供します。

最初の50ポイントは、当時のベストスコアの回答に対してA. Rexに授与されました。

2番目の100ポイントも同じ回答に対してA. Rexに授与されました。既存の回答に非常に良い説明を追加したためです。

次の賞金である200ポイントは、次のいずれかに授与されます

  • 新しい手法を使用した競争力のある回答。(これは賞金の対象となるのは私の担当者なので、私の主観的な判断に基づきますが、あなたは私が公正であると信じることができます。最高得点を取るのではなく、既存の回答と比較して合理的にうまくいく必要があります。リカレントニューラルネットワークに基づいたソリューションを探していますが、現在のトップスコアを支配しているマルコフモデルとは十分に異なると思われるものには賞金を授与します。

または:

  • A. Rexのトップスコア(現在は444444)を勝ち取った他の誰でも、あらゆる方法を使用します。

200ポイントの賞金が請求されたら、400ポイントを提供し、それに応じて要件を更新します。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
デニス

9
xkcd.com/1960はこの課題への参照のようです!
A.レックス

私はこれを圧縮することを考えました...しかし、私のコンピューターがすくめ
成代

回答:



97

Node.js、2 * 224 + 524279 = 524727

スコアの更新については、この投稿の最後にある変更ログを参照してください。

バイトを取得して返す関数。

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

これは、最後の8文字を調べて次の8文字を予測する単純なPPMモデルで構成されています。

少なくともT [L]回遭遇したとき、長さLのパターンを信頼します。ここで、T[1,1,2,1,1,2,3,5,2]の任意のしきい値の配列です。さらに、最初の文字がに​​一致するパターンを常に信頼します。[A-Z '"(]

最長の信頼できるパターンを選択し、呼び出し時にこのパターンに関連付けられた最高スコアの予測を返します。

ノート

  • これは明らかに速度が最適化されていませんが、私のラップトップでは約15秒で実行されます。

  • モデルをリセットせずにプロセスを連続して数回繰り返すことが許可された場合、エラーの数は5回の反復後に〜268000に収束します。

  • 予測関数の現在の成功率は〜56.8%です。コメントで@immibisが気づいたように、悪い推測と正しい推測が混ざっていると、結果はほとんど読めません。

    たとえば、本の終わり近くにあるこのスニペット:

    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.
    

    になる:

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

    悪い推測を下線で置き換えることにより、関数が何を正しくしたのかをよりよく理解できます。

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

    注意:上記の例は、入力ファイルの最初のバージョンで動作する以前のバージョンのコードで作成されました。

テストコード

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

変更ログ

  • 524727 -whale2.txtに切り替えて19644ポイントを節約(チャレンジ更新)
  • 544371-大文字、引用符、二重引用符、または開き括弧で始まるパターンを常に信頼するように強制することにより、327ポイントを節約
  • 544698-スペースで始まるパターンを常に信頼するように強制することにより、2119ポイントを節約しました
  • 546817-しきい値を調整し、予測関数をゴルフすることで47ポイントを節約
  • 546864-最大パターン長を8文字に延長して1496ポイントを節約
  • 548360-信頼できるパターンの概念を導入することで6239ポイントを節約しました。しきい値はその長さに依存します
  • 554599-改行予測を改善して1030ポイントを節約
  • 555629-予測関数をゴルフすることで22ポイントを節約
  • 555651-予測関数をゴルフすることで40ポイントを節約
  • 555691-初期スコア

44
好奇心が強い人にとっては、いや、これはMoby Dickのようなものを生み出しません。たくさんあり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ます。いくつかの完全な単語を取得することもあります。のようなwhales
イミビス

23
@immibisチャレンジのタイトルは賢明に選ばれました。これは、モビーディックで。:-)
アーナウド

3
@Nathaniel多くの更新がありましたので、それはかろうじて読めるだけで、あまり有益ではありません。代わりに、改善点について簡単に説明した変更ログを追加しました。
アーナウド

45
あなたのプログラムは実際にゲール語への完璧な翻訳を行っていると思います。
ベスカ

1
@ Draco18sこのコンマが良い推測であったか悪い推測であったかを判断するのは困難です。推測が間違っていた場合、予測関数は、受け取ったコンマの代わりに実際に存在する他の文字の後に、文字を合法的に配置しようとした可能性があります
アーナウド

91

Perl、2・70525 + 326508 = 467558

予測子

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

このプログラムを実行するには、次のものが必要ここで、このファイルという名前を付ける必要があります、B。(上記の文字の2番目のインスタンスでこのファイル名を変更できますB。)このファイルの生成方法については、以下を参照してください。

プログラムは、user2699によるこの回答のように、基本的にマルコフモデルの組み合わせを使用しますが、いくつかの小さな変更を加えています。これにより、次のキャラクターの分布が作成れます。情報理論を使用して、エラーを受け入れるか、またはBヒントをエンコードする際にストレージのビットを使用するかを決定します(そうであれば、どのように)。算術コーディングを使用して、モデルの小数ビットを最適に保存します。

プログラムの長さは582バイト(不要な最終改行を含む)で、バイナリファイルのB長さは69942バイトです。したがって、複数のファイルのスコアリングのルールではL、582 + 69942 + 1 = 70525 とスコア付けされます

このプログラムには、ほぼ確実に64ビット(リトルエンディアン?)アーキテクチャが必要です。m5.largeAmazon EC2のインスタンスで実行するには約2.5分かかります。

テストコード

# 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

テストハーネスは、提出がファイルsubmission.plにあると想定していますが、これは2行目で簡単に変更できます。

テキスト比較

"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

このサンプル(別の回答で選択)は、本文のかなり後の方にあるため、この時点でモデルはかなり発展しています。モデルには、文字を推測するのに直接役立つ70キロバイトの「ヒント」が追加されていることに注意してください。上記のコードの短いスニペットだけで駆動されるわけではありません。

ヒントを生成する

次のプログラムは、上記の正確な送信コード(標準入力)を受け入れ、上記の正確なBファイル(標準出力)を生成します。

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

同様の計算を実行するため、送信とほぼ同じ時間がかかります。

説明

このセクションでは、このソリューションが何をするのかを十分に詳しく説明し、「自宅で試してみる」ことができます。この答えを他の答えと区別する主なテクニックは、「巻き戻し」メカニズムとして数セクション下にありますが、そこに到達する前に、基本を設定する必要があります。

ソリューションの基本的な要素は言語モデルです。私たちの目的にとって、モデルとは、ある程度の英語のテキストを受け取り、次の文字の確率分布を返すものです。モデルを使用すると、英語のテキストはMoby Dickの(正しい)プレフィックスになります。必要な出力は分布であり、最も可能性の高いキャラクターの単なる推測ではないことに注意してください。

私たちの場合、この回答ではuser2699によるモデルを基本的に使用しています。Anders Kaseorgによる最高得点の回答(独自の回答以外)のモデルは、単一の最良の推測ではなく分布を抽出できなかったため、使用しませんでした。理論的には、その答えは重み付き幾何平均を計算しますが、文字通りに解釈するとやや悪い結果になりました。私たちの「秘密のソース」はモデルではなく、全体的なアプローチであるため、別の答えからモデルを「盗みました」。誰かが「より良い」モデルを持っているなら、残りのテクニックを使ってより良い結果を得ることができるはずです。

注釈として、Lempel-Zivなどのほとんどの圧縮方法は、この方法で「言語モデル」と見なすことができますが、少し目を凝らす必要があるかもしれません。(Burrows-Wheeler変換を行うものには特に注意が必要です!)また、user2699によるモデルはマルコフモデルの修正であることに注意してください。本質的に、この課題やおそらくテキスト全般のモデリングに対して競争力のあるものはありません。

全体的なアーキテクチャ

理解するために、アーキテクチャ全体をいくつかの部分に分割すると便利です。最高レベルの観点から、少しの状態管理コードが必要です。これは特に興味深いものではありませんが、完全を期すために、プログラムが次の推測を求められるたびに、Moby Dickの正しいプレフィックスが利用できることを強調したいと思います。過去の誤った推測は一切使用しません。効率のために、言語モデルはおそらく最初のN文字の状態を再利用して最初の(N + 1)文字の状態を計算できますが、原則として、呼び出されるたびにゼロから再計算できます。

プログラムのこの基本的な「ドライバー」を脇に置き、次の文字を推測する部分を覗いてみましょう。概念的には3つの部分を分離するのに役立ちます:言語モデル(上記)、「ヒント」ファイル、および「インタープリター」。各ステップで、インタープリターは言語モデルに次の文字の分布を要求し、ヒントファイルからいくつかの情報を読み取ります。次に、これらの部分を組み合わせて推測します。ヒントファイルに含まれる情報とその使用方法については、後で詳しく説明しますが、現時点では、これらの部分を精神的に分離しておくと役立ちます。実装面では、ヒントファイルは文字通り別個の(バイナリ)ファイルですが、文字列またはプログラム内に保存されたものである可能性があることに注意してください。近似として、

この回答のようにbzip2などの標準的な圧縮方法を使用している場合、「ヒント」ファイルは圧縮ファイルに対応します。「インタープリター」は圧縮解除プログラムに対応し、「言語モデル」は少し暗黙的です(上記のとおり)。

ヒントファイルを使用する理由

さらに分析するために簡単な例を選択しましょう。テキストがN長い文字であり、すべての文字が(独立して)E半分よりわずかに小さい確率で、T同様に半分より少し小さいA確率で、確率1/1000 = 0.1%であるモデルによって近似されていると仮定します。他の文字は使用できないと仮定しましょう。いずれにせよ、これAは以前に目に見えなかったキャラクターが青から外れている場合とよく似ています。

我々は(この質問に対する他の回答の、すべてのほとんどではなくがそうであるように)L 0政権で操作した場合、のいずれかを選ぶよりも、通訳のためには良い戦略がないEとはT。平均して、約半分の文字が正しくなります。したがって、E≈N / 2であり、スコア≈N / 2です。ただし、圧縮戦略を使用する場合は、文字ごとに1ビットを少し超える量まで圧縮できます。Lはバイト単位でカウントされるため、L≈N / 8になり、スコア≈N / 4になり、前の戦略の2倍になります。

このモデルで1文字あたり1ビットを少し超えるこのレートを達成するのはわずかではありませんが、1つの方法は算術コーディングです。

算術コーディング

よく知られているように、エンコードはビット/バイトを使用して一部のデータを表す方法です。たとえば、ASCIIは英語のテキストと関連文字の7ビット/文字エンコーディングであり、検討中の元のMoby Dickファイルのエンコーディングです。一部の文字が他の文字よりも一般的である場合、ASCIIのような固定幅のエンコードは最適ではありません。このような状況では、多くの人がハフマンコーディングに手を伸ばします。これは、文字あたりのビット数が整数の固定(プレフィックスなし)コードが必要な場合に最適です。

ただし、算術コーディングはさらに優れています。おおまかに言って、「分数」ビットを使用して情報をエンコードすることができます。オンラインで利用可能な算術コーディングに関する多くのガイドがあります。オンラインで利用できる他のリソースがあるため、ここでは詳細をスキップします(特に実用的な実装については、プログラミングの観点からは少し注意が必要です)。

既知の言語モデルによって実際に生成されたテキストがある場合、算術コーディングは、そのモデルからのテキストの本質的に最適なエンコーディングを提供します。ある意味で、これはそのモデルの圧縮問題を「解決」します。(したがって、実際には、主な問題はモデルが不明であり、一部のモデルは人間のテキストのモデリングにおいて他のモデルより優れているということです。) 、この課題の解決策を生み出す1つの方法は、算術エンコーダーを使用して言語モデルから「ヒント」ファイルを生成し、算術デコーダーを「インタープリター」として使用することでした。

この本質的に最適なエンコーディングでは、確率pの文字に-log_2(p)ビットを費やすことになり、エンコーディングの全体的なビットレートはシャノンエントロピーになります。つまり、確率が1/2に近い文字はエンコードに約1ビットかかり、確率が1/1000である文字は約10ビットかかります(2 ^ 10は約1000ビットであるため)。

しかし、この課題のスコアリングメトリックは、最適な戦略としての圧縮を避けるために適切に選択されました。短いヒントファイルを取得するためのトレードオフとして、いくつかのエラーを作成する方法を見つけ出す必要があります。たとえば、試みる可能性のある戦略の1つは単純な分岐戦略です。可能な場合は一般に算術エンコードを使用しようとしますが、モデルの確率分布が何らかの形で「悪い」場合、最も可能性の高い文字を推測して、エンコードしてみてください。

なぜエラーを起こすのですか?

以前から例を分析して、「意図的に」エラーを発生させたい理由を動機付けましょう。算術符号化を使用して正しい文字をエンコードする場合、Eor の場合は約1ビットを使いますが、の場合はT約10ビットを使いますA

全体として、これはかなり良いエンコードであり、3つの可能性がありますが、文字ごとに少し以上を費やします。基本的に、これAはかなり可能性が低く、対応する10ビットをあまり頻繁に使うことはありません。ただし、A?の場合に代わりにエラーを作成できたらいいと思いませんか?結局のところ、問題のメトリックは、1バイト= 8ビット長が2エラーに相当すると見なします。したがって、文字に8/2 = 4ビット以上を費やすのではなく、エラーを好むように思われます。1バイト以上を費やして1つのエラーを保存すると、間違いなく最適とは言えません!

「巻き戻し」メカニズム

このセクションでは、このソリューションの主な巧妙な側面について説明します。これは、長さのコストをかけずに誤った推測を処理する方法です。

分析している簡単な例では、巻き戻しメカニズムは特に簡単です。インタープリターは、ヒントファイルから1ビットを読み取ります。0の場合、推測しEます。1の場合、推測しTます。次に呼び出されたときに、正しい文字が何であるかを確認します。ヒントファイルが適切に設定されていれば、EまたはのT場合、インタープリターが正しく推測することを保証できます。しかし、どうAですか?巻き戻しメカニズムの考えは単にコード化Aしないことです。より正確には、インタプリタが正しい文字がであることを後で知った場合、それは比phorA的に「テープを巻き戻します」:以前に読んだビットを返します。読み取ったビットは、コーディングEまたはT、 しかし今ではありません; 後で使用されます。この単純な例では、これは基本的に、正しい文字になるまで同じ文字(または)推測し続けること意味します。その後、別のビットを読み取り、続行します。ET

このヒントファイルのエンコードは非常に簡単です。sを完全に無視しながら、すべてのEsを0ビットに、Tsを1ビットに変換しAます。前のセクションの最後の分析により、このスキームではいくつかのエラーが発生しますが、Asのいずれもエンコードしないため、全体のスコアが低下します。より小さな効果として、実際にはヒントファイルの長さも節約されます。これは、ビットごとにわずかではなく、それぞれに対してEとビットを正確に1ビット使用Tするためです。

少し定理

エラーをいつ発生させるかをどのように決定しますか?モデルが次の文字の確率分布Pを与えると仮定します。可能な文字を、コード化されたコードコード化されいない 2つのクラスに分けます。正しい文字がコーディングされていない場合、「巻き戻し」メカニズムを使用して、長さのコストなしでエラーを受け入れます。正しい文字がコーディングされている場合、算術コーディングを使用してエンコードするために、他の分布Qを使用します。

しかし、どのディストリビューションQを選択する必要がありますか?コード化された文字がすべて、コード化されていない文字よりも高い確率(P)を持っていることを確認するのはそれほど難しくありません。また、ディストリビューションQにはコード化された文字のみを含める必要があります。結局、他のものをコーディングしていないので、それらにエントロピーを「使う」べきではありません。確率分布Qがコード化文字のPに比例する必要があることを確認するのは少し複雑です。これらの観察結果をまとめると、最も可能性の高い文字をコーディングする必要がありますが、それほど可能性の低い文字はコーディングしないでください。また、Qはコーディングされた文字でPを再スケーリングします。

さらに、コーディング文字にどの「カットオフ」を選択するべきかについてのクールな定理があることがわかります。他のコーディングされた文字の少なくとも1 / 5.393の可能性がある限り、コーディングする必要があります。これ5.393は、上記のプログラムの終わり近くで一見ランダムな定数の出現を「説明」します。1 / 5.393≈0.18542という数値は、方程式-p log(16)-p log p +(1 + p)log(1 + p)= 0の解です。

おそらく、この手順をコードで記述することは合理的な考えです。このスニペットは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;
}

すべてを一緒に入れて

前のセクションは残念ながら少し技術的ですが、他の部分をすべてまとめると、構造は次のようになります。指定された正しい文字の後の次の文字を予測するようにプログラムが要求されるたびに:

  1. Moby Dickの既知の正しいプレフィックスに正しい文字を追加します。
  2. テキストの(Markov)モデルを更新します。
  3. 秘密のソース:以前の推測が間違っていた場合は、巻き戻し、前の推測前の状態に算術復号器の状態を!
  4. 次の文字の確率分布Pを予測するためにマルコフモデルに依頼します。
  5. 前のセクションのサブルーチンを使用して、PをQに変換します。
  6. 算術デコーダーに、分布Qに従って、ヒントファイルの残りの部分から文字をデコードするように依頼します。
  7. 結果の文字を推測します。

ヒントファイルのエンコードも同様に動作します。その場合、プログラムは正しい次の文字が何であるかを知っています。コーディングする必要がある文字である場合、もちろん算術エンコーダーを使用する必要があります。ただし、コード化されていない文字の場合、算術エンコーダーの状態は更新されません。

確率分布、エントロピー、圧縮、算術コーディングなどの情報理論的背景を理解しているが、この投稿を理解しようとして失敗した場合(定理が真である理由を除く)、私たちに知らせてください。読んでくれてありがとう!


8
うわー、印象的な答え。Bファイルを生成するために追加のコードが必要だと思いますか?もしそうなら、あなたの答えにそれを含めることができますか?
ナサニエル

8
優れた!50万得点の壁を突破する最初の(そしてこれまでのところ唯一の)答え。
シュ

5
「シテクジラを退治しました」omg私は泣いています
フィル

5
バウンティ期間中に新しい回答が投稿されなかったため、最高のスコアリングと最も洗練されたアプローチの両方として、あなたの回答にそれを授与します。あなたは時間があれば、私は考え本当に正確なアルゴリズムが何であるか、すなわち、この答えはどのように動作するかの深さのより詳細な説明に感謝しますか?
ナサニエル

2
@Nathaniel:この投稿に説明を追加しました。ソリューションを自分で再現できるほど詳細であると思われる場合はお知らせください。
A.レックス

77

Python 3、2・267 + 510193 = 510727

予測子

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]

これは、次数0、…、16マルコフモデルの重み付きベイジアン組み合わせを使用し、重みは[1、6、12、30、65、99、87、117、118、89、95、118、96、184、126、 219、126]。

結果はこれらの重みの選択にあまり敏感ではありませんが、候補者の多数派をまとめる」に対する回答で使用したのと同じレイトアクセプタンスヒルクライミングアルゴリズムを使用し、各候補の変異が単一の重量に対してわずか±1増分。

テストコード

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
仕事に最適なツール。素晴らしいスコア。良いですね。
-agtoever

1
明確化の可能性:b"\0\3\6\r\34'&-20'\22!P\n[\26"重みのASCII表現です。印刷できない小さな値は8進数でエスケープされます。
クール

あなたは(それが少し良く行う可能性があります)その上で、あなたのコードを再実行してみてください-私は、テキストがラップされていないファイルのバージョンとの質問を更新しました
ナサニエル・

3
その説明をありがとう-質問のあらすじを編集することができれば素晴らしいでしょう。(以前のチャレンジPaint Starry Nightでの経験は、これらの最適化手順が回答の最も興味深い部分であるため、回答にそのために使用されるコードとその説明が含まれている場合は、はるかに優れています。両方にルールを含めました。する必要があると言って課題。)
ナサニエル

1
@Christoph私のモデルの組み合わせは、実際には重み付けされた幾何平均です。しかし、ロジスティックドメインでのPAQの平均はわずかに異なります。それが良いかどうかを確認する必要があります。
アンデルスカセオルグ

55

パイソン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

オンラインでお試しください!

グローバル変数を使用する関数。単語レベルでモデルを構築しながら、学習します。この単語でこれまで見たものを考えると、最も一般的な次のキャラクターは何ですか?入力が増えると、テキストから一般的な単語がかなりよく学習され、次の単語を開始する最も一般的な文字も学習されます。

例えば:

  • これまでに表示されているのが「Captai」の場合、「n」
  • 「キャプテン」の場合、スペースを予測します
  • 単語の始まりで、最後の単語が「Captain」だった場合、「A」と予測されます
  • これまでの単語が「A」の場合、「h」(および「a」と「b」。「C」も同様)が予測されます。

最初はあまりうまくいきませんが、最後には実際の単語の大部分が出てきます。フォールバックオプションはスペースです。1文字のスペースの後は、先行する文字が「nedtfo」、数字、ハイフン、アポストロフィのいずれかでない限り、「a」になります。また、71文字以降の改行、または66文字以降にスペースが予期される場合、積極的に改行を予測します。これらは両方ともデータに合わせて調整されました(これらの6つの特殊なケースの外では、a "がより適切な推測です)。

どの単語のペアが一緒になったかを学び、マッピングを事前シードすることは価値がないことが判明しました。


最終的には次のようなテキストになります。

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!

入力のこの部分に対応します:

彼の周り。

「キャプテン・アハブがしたのとほぼ同じ瞬間に彼を見たので、叫んだ」とタシテゴは言った。

「同じ瞬間ではなく、同じではありません。ダブロンは私のものです。運命はダブロンを私のために予約しました。私だけです。 -彼女が吹く!

特に固有名詞がどこで出てくるかはかなりよくわかりますが、単語の終わりもほとんど正しいです。「dou」と表示されると「doubt」を期待しますが、「l」が表示されると「doubloon」になります。

同じモデルでもう一度実行すると、構築したばかりですぐに別の92k(51.7%-> 59.3%)が正しくなりますが、2回目以降は常に60%未満になります。


測定コードはTIOリンクにあります。または、ここで少し改善されたバージョンがあります。

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 最後に推測された出力があります。


3
これは素晴らしいアプローチです!
スカイラー

2
多すぎる<s> </ s>;)
FantaC

1
+1。このアプローチはLZW圧縮アルゴリズムを思い出させたからです。
マルコス

25

C ++、スコア:2 * 132 + 865821 = 866085

217バイトを節約してくれた@Quentinに感謝します!

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

文字を指定すると、入力文字の後に最も頻繁に現れる文字を出力するだけの非常に簡単なソリューション。

スコアを確認するには:

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

編集:使用whale2.txtするとより良いスコアが得られます。


5
この配列を文字列リテラルに変換し、文字列Lを保存する代わりに直接インライン化することができます:)
クエンティン

@クエンティンありがとう!今、私はそもそもなぜそれを考えなかったのか疑問に思っています
...-Steadybox

20

Python、2 * 516 + 521122 = 522154

アルゴリズム:

さらに別のpython提出、このアルゴリズムは、長さ1、...、lのシーケンスを見て最も可能性の高い次の文字を計算します。確率の合計が使用され、より良い結果を得るためのいくつかのトリックがあります。

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

結果:

ほとんどが意味不明ですが、「Father Mapple」などの不定期のフレーズで取り上げられています。

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

テストコード:

非常に簡単で、さまざまなポイントでテキストの例をいくつか出力します。whale2.txtを使用します。これにより、改行を計算するための余分なロジックが回避されます。

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
サイトへようこそ!これは素晴らしい最初の提出です。:)
DJMcMayhem

@DJMcMayhem、歓迎してくれてありがとう。私はしばらく見て楽しみました、これはエントリーのために私の注意を引く最初のコンテストです。
user2699

19

C(gcc)679787 652892

84 76バイト、679619 652740の誤った推測

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

オンラインでお試しください!

更新:更新されたファイルで約27000ポイント、より良いゴルフ機能を備えた16ポイント(8バイト)。

説明

これが機能する方法は、コードがテキストを実行するときに、特定の4文字シーケンスを終了した最後の文字を記憶し、その値を返すことです。上記のArnauldのアプローチに多少似ていますが、2つの与えられた4文字のシーケンスが同じ方法で終了するという固有の可能性に依存しています。

脱ゴルフ:

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

... TIOリンクは役に立ちません。関数は最後の割り当ての値を返しますか?
user202729

説明で答えを編集してから、:)

1
@Rogem de-golfedバージョンを追加しました(フォローできなかったため追加しました)。
アダムデイビス

@AdamDavisはほとんどのCの実装で、すべてのグローバル変数はゼロから始まります。未定義の動作であるため、code-golfでのみ使用されます。
-NieDzejkob

1
@NieDzejkobああ、そうですね、ありがとう!「ANSI-Cでは、すべての初期化されていない静的/グローバル変数を0で初期化する必要があります。」
アダムデイビス

16

sh + bzip2、2 * 364106 = 728212

2 * 381249 + 0 = 762498

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

続いて、最初のバイトが欠落したbzip2-compressed whale2.txtが続きます

入力を無視します。正解を出力します。これにより、一方のベースラインが提供されます。danieroは、反対側のベースラインを提供します。

ビルダースクリプト:

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

I / Oテストハーネス(tcc; gccの最初の行を切断)。このテストハーネスは、読み取り/書き込みI / Oを予期する完全なプログラムを送信する適切なプラットフォーム上の誰でも使用できます。不正を回避するために、バイト単位のI / Oを使用します。子プログラムは、ブロックを回避するために、バイトごとに出力をフラッシュする必要があります。

#!/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
彼が求めているのは、この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.条項に違反しないことだと思います

8
@Rogem圧縮されたデータはここに示されているものの後に置かれ、コードはそれ自体にアクセスします。
user202729

4
:質問は述べています。それがために呼び出されたときにあなたの提出がプログラムまたは複数回呼び出されるか、呼び出される関数(など)になります」nthそれはのn番目の文字が与えられます、時間whale.txtwhale2.txt、それがために、出力にその推測をしなければなりません(n+1)thキャラクター。" -この要件はどのように達成されますか?コードは、whale.txt実行されるたびにテキスト全体を表示します。
axiac

1
@axiac「次の入力バイトを受け取る前にプログラムが常に1バイトの出力を提供する限り、何でも構いません。」
-user202729

5
@axiacがテストハーネスを与えられたので、STDINからプログラムを1バイト送信することを「呼び出しまたは呼び出し」と見なします。インポートの重要な点は、プログラムが入力の各バイトの後に1バイトの出力を返すことです。これは、テストハーネスを介して実行されたときに実際に実行されます。質問が言うように、「プログラムが常に次の入力バイトを受け取る前に1バイトの出力を提供する限り、何でも構いません。」
ナサニエル

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

オンラインでお試しください!


... ///スペースを表示する答えは10のアップ投票を取得しますが、私のコードは3つしか取得できません...

説明:

各キャラクターについて、プログラムは次のことを行います。

  • 増加する frequency[prev][char]
  • で最も頻繁に登場するキャラクターを見つける frequency[char]
  • それを出力します。

  • TIOリンク内の未ゴルフコード、コメントアウト。
  • コードは131バイトです。
  • 私のマシンで実行されるコードは以下を報告します:
879504 / 1215235
Time: 62.01348257784468

合計スコアがあります

2*131 + 879504 = 879766

大きなファイルをTIOにアップロードする方法がないため(デニスに尋ねる場合を除く)、TIOリンクで実行する例では、テキストのごく一部のプログラムのみを実行します。

以前の回答と比較すると、これには362個の不正な文字がありますが、コードは255バイト短くなっています。乗数により、提出物のスコアが低くなります。


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

このアプローチでは、ルックアップテーブルを使用して、特定の文字列に続く最も一般的な文字を学習します。ルックアップテーブルのキーは最大4文字であるため、関数は最初に現在のキャラクターでルックアップテーブルを更新し、次に現在の文字を含む4つの前の文字の後に発生する可能性が最も高い文字をチェックします。 。ルックアップテーブルにこれらの4文字が見つからない場合、スペースが印刷されます。

このバージョンでは、whale2.txt成功した推測の数が大幅に改善されるため、ファイルが使用されます。

クラスのテストに使用されるコードは次のとおりです。

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

コードはわずか2秒で実行されます。記録のために、これはルックアップテーブルのキーのサイズを変更したときに得られるものです(モデルをリセットせずに2回目の実行の結果を含む)。

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

このアルゴリズムで4文字のキーサイズが最良の選択である理由を知ることは興味深いでしょう。

テキスト比較

元の:

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

再作成:

"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 

推測:

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

変更ログ

  • 569279-変更whale2.txtされ、最適化が削除されました。
  • 577366-改行を返すタイミングを推測しようとするコードで最適化されました。
  • 590354-オリジナルバージョン。

4
キーサイズと列のしきい値を変更すると、分散を表示してくれてありがとう!
ジェレミーワイリッヒ

テキストが折り返されていないバージョンのファイルで質問を更新しました-おそらくそれを使用していくつかのポイントを保存できます
ナサニエル

@Nathaniel確かにそうです。答えを更新しました。
チャーリー

型を宣言する代わりに、varを使用していくつかのバイトを保存できます。
エドT

1
キーのサイズが大きくなると、ヒットとミスの数が減るため、短いキーが正しい文字を推測した場合により多くのスペースが出力されます。キーサイズが小さくなると、一致するセグメントの個々の推測の精度が低下します。これが4の長さが最適な理由だと思います。複数の長さのキーを維持し、長いキーが利用できないときに短い一致を使用すると、長いキーの長さでヒット率(およびスコア)が大幅に改善されると思います。
ジェフリーLホイットレッジ

11

Java 7、1995文字、(1995 * 2 + 525158)529148

Javaは、プログラムのサイズが小さい場合はダメです。とにかく、非常に複雑でトリッキーなアプローチをいくつか試してみましたが、驚くほどくだらない結果が得られました。その後私は戻って、単純なアプローチを行っただけで、プログラムのサイズが小さくなり、結果が改善されました。

このアプローチは実際には非常に簡単です。(それらの文字のすべての部分文字列に加えて)前のx文字を盲目的にハッシュテーブルに送り、現在の文字にマップします。次に、現在のキャラクターを最も正確に予測するパターンを追跡します。特定の文字に先行するパターンが複数回検出された場合、文字の予測に成功します。長い文字列が優先され、指定された文字列の後に最も頻繁に続く文字が優先されます。このアルゴリズムは、ドキュメントの種類や英語については何も知りません。

9文字を使用し、可能な場合は前の9文字内の単語全体を一致させようとしました。文字列内で単語のマッチングを行わない場合、最適な長さは6文字で、さらに数千の予測ミスが発生します。

興味深い観察結果の1つは、20文字を使用すると、最初は間違った予測が行われましたが、その後のパスでは99.9%の精度であったことです。アルゴリズムは基本的に、重複する20バイトのチャンクで本を記憶することができました。これは、本全体を一度に1文字ずつ呼び出すことができるほど明確でした。

  • (1950 * 2 + 532919)536819
  • (2406 * 2 + 526233)531045句読点のチェックによる推測の改善
  • (1995 * 2 + 525158)529148さらに微調整し、いくつかの冗長な表現を削除

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

これは、このような冗長な言語ではかなり良いスコアです。
DJMcMayhem

1
ファイルのサイズはプログラムのサイズに比べて改善の余地が多いため、一見の価値があると考えました。
ジムW

3
これは、Java 7(または価値のあるJavaバージョン)ではコンパイルできません。コードを修正していただけますか?それが終わったら、ゴルフを喜んでゴルフして、スコアを改善します。
オリビエグレゴワール

テストされていませんが、これはあなたのまったく同じコードで、わずかにゴルフされた950バイトでなければなりません。ただし、現在のコードにはかなりの数のエラーが含まれているため、すべてを正しく入力したかどうかはわかりません。繰り返しますが、テストされていないので、バージョンを比較して変更/名前の変更を確認し、すべてが元のコードと同じように機能するかどうかを確認してください。しかし、間違いなくもう少しゴルフができます。
ケビンクルーッセン

クラップ、これは私の古い仕事に退屈している間に行ったもので、コードを持っていませんでした。タイプミスがどこにあるかを見るために私はそれを見てみる必要があります。
ジムW

10

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

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

私はこれを独立して試しましたが、マイケル・ホーマーの答えの実質的に劣ったバージョンになりました。それが私の答えを完全に時代遅れにしないことを願っています。

これにより、時間の経過とともに単語の辞書が作成されます(または\n、大文字と小文字を区別し、句読点を含む文字列として粗く定義されます)。次に、現在の単語についてこれまでにわかっていることで始まる単語をこの辞書で検索し、結果のリストを出現頻度で(ゆっくりと)ソートし、次の文字が最もよく一致する単語の次の文字であると推測します。最も一般的な一致する単語が既にある場合、または一致する単語が存在しない場合は、を返します

また、単語ペアのうんざりするほど非効率的な辞書を構築します。単語の境界に達すると、次の文字は、最も一般的な一致する単語のペアの2番目の単語の最初の文字であるか、一致しtない場合を推測します。ただし、あまり賢くはありません。続いてMoby、プログラムは正常に次の文字があることを推測しDますが、それはすべてのコンテキストについて忘れて普通語「オランダ」は、テキストの前半に、より頻繁にあるように思わので、クジラ、「モービーダック」を(呼び出して終了します)。個々の単語よりも単語のペアに優先順位を付けることでこれを修正するのは簡単ですが、ゲインはわずかです(通常、3番目の文字からは正しいので、そもそも単語のペアは役に立たないため)。

提供されたテキストによりよく一致するようにこれを調整することはできましたが、入力の事前知識に基づいてアルゴリズムを手動で調整することは実際にはゲームの精神ではないと思います。そしておそらく私もそうすべきではなかったでしょう)、私はそれを避けました。入力ファイルの既知の行の長さを無視し、代わり\nに13スペースごとに挿入しました。これはほぼ間違いなく非常に貧弱な一致です。主な目的は入力と一致するのではなく、行の長さを維持することでした

コードは正確に高速ではありません(私のマシンでは2時間以内)が、全体で文字の約半分(49%)が正しく取得されます。で実行した場合、スコアはわずかに向上すると予想していますがwhale2.txt、まだ実行していません。

出力の開始は次のようになります。

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

しかし、最後には、もう少し...何かのように見えます。本の終わり近くからの私のお気に入りの一節、

どちらも私のものではありえないので、あなたを追いかけながら、あなたを縛りながら、あなたはクジラをくじきました!このように、私は槍をあきらめます!」

として出てくる

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 "

それはカーンの怒りをもっと混乱させたでしょう。そして、「孤独」→「可ting」は特に満足のいく代替です。

編集:余分なスペースを削除して1バイトを保存しました

得点

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

これにより、Moby Dickのテキストに対してプログラムが実行され、「予測された」テキストがstdoutに出力され、stderrを悪用してスコアが書き込まれます。出力をファイルにリダイレクトすることをお勧めします。


2
PPCGへようこそ!
マーティンエンダー

1
lambda i:i[1]対処するよりも安くはないでしょうoperatorか?
ドラコニス

@Draconisほぼ間違いなく。
ジョージワトソン

9

C ++、2・62829 + 318786 = 444444

このプログラムを実行するには、次のものが必要ここで、このファイルという名前を付ける必要があります、C

プログラムは、以前の回答と同じマルコフモデルの組み合わせを使用します。前と同様に、この組み合わせは本質的にuser2699によるこの回答からのモデルですが、いくつかの小さな変更が加えられています。

この回答が以前とまったく同じモデルをどのように使用するかを見ると、改善は前述の「巻き戻し」メカニズムよりも優れた情報理論的メカニズムです。これにより、組み合わせた長さを小さくしながら、エラーを減らすことができます。プログラム自体は、スコアに大きく寄与していないため、あまりゴルフされていません。

プログラムの長さは2167バイト(インデント用のすべてのタブとその他の不要な文字を含むが、テストコードの前)であり、バイナリファイルのC長さは60661バイトです。したがって、複数のファイルのスコアリングのルールではL、2167 + 60661 + 1 = 62829。

このプログラムはm5.4xlargeAmazon EC2のインスタンスで実行するのに約8分かかり、16 GBを少し超えるメモリを使用します。(この過剰なメモリ使用量は必要ありません-私たちもそれを最適化しませんでした。)

#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バイト、526092エラー(を使用whale2.txt)。これは間違いなくさらに改善することができますが、「投稿するのに十分な」段階に達しました。

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

アイデアは、2、3、4、...、10文字のすべての実行の頻度を保存することです。これらの長さLのそれぞれについて、最新のL-1文字が保存されたパターンと一致するかどうかを確認します。もしそうなら、我々の推測g Lはそのパターンに続く最も頻繁な次の文字です。このようにして、最大9つの推測を収集します。どの推測を使用するかを決定するために、各パターンの頻度をその長さの8乗で重み付けします。重み付き周波数の合計が最大になる推測が選択されます。一致するパターンがない場合、スペースを推測します。

(最大パターン長と重み指数は、試行錯誤によって選択され、誤った推測を最小限に抑えます。)

これが私の未開発の進行中のバージョンです。

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

そしてテストハーネス:

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

以下は、テキストの冒頭付近からのサンプル出力です。すでに私たちは、彼らの最初の文字を見た後に一般的な単語を終了する能力を見始める(intoandby、また、明らかに、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 

終わり近くでは、まだ多くの間違いがありますが、非常に優れたシーケンスもたくさんあります(shmage seashawksたとえば、)。

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

いくつかの間違いを見て、アルゴリズムが「期待する」単語を推測するのは興味深いことです。たとえば、の後 sailに、プログラムは両方の時間を予測しますo--for sailor、私は推測します。または、, a予想される後、nおそらくの一般的な出現のため, and


変更ログ:

  • 274 * 2 + 526092 = 526640アルゴリズムをゴルフしましたが、いくつかの余分なエラーが発生しました
  • 306 * 2 + 526089 = 526701オリジナルバージョン

6

Python 2、スコア:2 *(407 + 56574)+ 562262 = 676224

出現回数でソートされた、テキストで使用されているほとんど すべての 単語のリストから、前の文字に一致する単語を検索します。

コード:

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"

データ:https : //www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0

テストスイート:

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

編集:使用whale2.txtするとより良いスコアが得られます。


5

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

さらに別のプレフィックス周波数送信。で実行しwhale2.txt、他の人と同様の(わずかに悪い)スコアを取得します。

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

これは、履歴の接尾辞で始まる最長の文字列を貪欲に検索し、複数の候補がある場合は、短い文字列でタイブレークします。

例:最後の7つの文字がある場合はabcdefgh、文字列abcdefghiabcdefghjフォームのすべての文字列で最大周波数が表示されabcdefgh*、出力がどちらかになりますij、短い接尾辞(とのタイブレークbcdefghcdefgh、...)。

不明な理由により、7を超えるものがあり、コンピューターには実行に十分なRAMがありません。7でも、実行するにはすべてのWebブラウザーを閉じる必要があります。


テストコード:

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

ゴルフをしていない:

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

出力例:

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

これはテキストの終わり近くです。最も長い単語はかなり正確に予測されています(intervalspivot-holedistance

 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

大文字は良くないようです。



...また、実装が困難です。
user202729

4

Python 2、756837

マルコフ連鎖かもしれない何かを使用していますか?

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
簡単な説明:はにzlib.decompress('...')評価され{'G?':' ', 'G;':' ','G"':' ',.......}a2文字から1文字にマップする辞書です。Steadybox's answerの基本的に2文字のバリアント。
user202729

1
ご覧のとおり、リテラルは17780バイトです。圧縮解除されたコンテンツ内の空白を削除することにより、11619文字に減らすことができます。これにより、12322バイトが節約されます。(正しくカウントした場合)また、... 16進エスケープコードを実際の生の文字に変換すると、さらに多くのバイトを節約できます。
user202729

生のバイトの場合、ここに投稿するにはどうすればよいですか?
スカイラー

1
xxdhexdumpuuencode、または類似
ピーター・テイラー

@ user202729 Pythonコードには実際の生のNULバイトを含めることができないことに注意してください。
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)

例:

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

事前に計算された3つの補助ファイルを使用します。

  • seqwords 236の最も一般的な単語が含まれています。
  • wordsseq これらの単語のLZMA圧縮シーケンス、および最も一般的な236以外のすべての単語の長さが含まれています。
  • dicttries各語長について、残りのすべての語を含む決定木が含まれます。これらの試行から、進むにつれてエントリが選択されます。

このようにして、他のすべての不可逆スキームよりも大幅に低いエラー率を達成します。残念ながら、wordsseqファイルはまだ大きすぎて競争力がありません。

ファイルを作成して分析を行う完成バージョンは次のとおりです。

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;

現状では、このソリューションはWIPであり、したがって無償です。また、実際のコードサイズがスコアにほとんど影響を与えないことを考慮して、マイクロ最適化を開始する前に最初に回答を投稿すると思いました。
(完全なコードはこちらから入手可能:https : //github.com/BrainStone/MobyDickRNG-完全なプログラムとシード検索を含む)

このソリューションはRNGに基づいています。最初にテキストを分析します。2つの連続した文字の出現をカウントするマップを作成します。次に、分布図を作成します。これはすべて静的に行われるため、ルールに従う必要があります。

次に、テキストを印刷しようとするときに、ルックアップを行い、可能な文字のランダムな文字を引き出します。これは通常、最も一般的な後続の文字を出力するよりも悪い結果を生成しますが、より良い結果を生成する神の種が存在する可能性があります。そのため、シードはハードコーディングされています。私は現在、最高の種を探しています。そして、より良い種が見つかったら、この回答を更新します。投稿してください!

誰かが種子を自分で検索したり、別のRNGを使用したい場合は、気軽にレポをフォークしてください。

スコアの計算に使用される方法:https : //github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15

現時点では合計スコアは最悪ですが、スペースを出力するだけのエラーカウントを上回っています。そして、より多くのシードをチェックすることにより、スコアが低下する可能性が高くなります。

変更履歴

  • 2018/01/24初期回答を掲載しました。
    チェック済みシード:0-50000。スコア:2305 * 2 + 1017754 = 1022364
  • 2018/01/24:最小限のゴルフをしました。スコア計算方法へのリンクを追加しました。
    チェック済みのシード:0-80000。スコア:1920 * 2 + 1017754 = 1021594(-770)
  • 2018/02/02:新しいシード(10087702)(サブミッションを修正する時間を見つけられませんでした)
    チェックされたシード:0-32000000。スコア:1923 * 2 + 1017344 = 1021190(-404)

スコアを評価するテストハーネスを回答に含めることができますか?
ナサニエル

@Nathanielスコアコードを直接リンクしました。リポジトリに加えて、これで十分だと思いますか?
BrainStone

ルールを確認すると、それらの一部に違反していることに気付きました。私は問題を解決したら回答を更新します
-BrainStone

次に、テキストをランダムシードにエンコードします。難解なプログラミング言語のSeedを参照してください。MT19937プログラムをリバースエンジニアリングし、この答えを(できれば)破ることができます。
user202729

良いアイデアですが、良いスコアを取得するのに役立ちません。とにかく+1。
user202729

3

ルビー、1164418(痛い)

他の答えをチェックせずに自分がどれだけうまくできるかを見たかっただけです。
ファイルを分析して生成されたリテラルが含まれているため、これが許可されているかどうかはわかりませんが、たとえそうでなくても、誰かをbeる危険にさらされているわけではありません。

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

生成方法 x

最初に、私a.txtは以下で生成しました:

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

次に生成しましたa.csv

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

次にx、次の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

得点方法

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

私はそれが許可されていると確信しています。ファイル分析した場合、良い仕事です!プログラムがそうする場合にのみ、これは無効です。
エリックアウトゴルファー

@EriktheOutgolfer> _>(タイトルに「(非競合)」を
静かに挿入します

どうして?これが有効である場合、それは多くは勝てないかもしれませんが、競合しています。無効な場合(つまり、ソリューションがファイルから読み取り、リテラルを含んでいない場合)、削除する必要があります。
エリックアウトゴルファー

うーん。ソリューションだけでなく、ファイルを分析するプログラムがあれば、それはあなたの意図だと思いました。
NO_BOOT_DEVICE

1
Rubyは読めませんが、これは妥当だと思います。プログラム内にリテラルを含めることはまったく問題ありません。まったく問題ありません。
ナサニエル

2

Python 3、(146 * 2 + 879757)880049バイト

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

オンラインでお試しください!

かなり簡単な頻度表。文字列の各位置は、現在の文字のASCIIコード(マイナス10 = 0x0a = '\ n'、ファイルの最下位文字)に対応し、各インデックスの文字は次の最も頻繁な文字です。周波数を正しく計算したと仮定すると…

user202729のテストのコードでテスト済み


を使用していくつかのバイトを保存できますdef f(c):return(" ">c)*c or"t ... e"[ord(c)-32]か?
ニール

0

[Python 3](644449 * 2 + 0)1288898ポイント

たった 644449バイトで完璧な精度

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

完全なコードは回答に収まらないため、ここに配置し、回答テキストで大きなバイナリ文字列リテラルをb '###'に置き換えました。

これは次のコードで生成されます。「modified.py」は生成されたファイル、「cheatsheet.txt」は2文字目から始まるwhale2.txtファイルです。

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

「modified.py」の末尾に次を追加することで、コードを実行できます。「whale2.txt」は「modified.py」と同じディレクトリにある必要があり、出力は「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)

この回答は、whale.txtまたはwhale2.txtのいずれにも直接アクセスしません。ルールで明示的に許可されている既存の標準圧縮ライブラリを使用します。


そこに「\ r \ n」があり、Windowsでそれらを数えたときに取り除くことができなかった可能性があります
Legorhin

2
ええ、それは広まったタイプミスでした
レゴリン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.