Напиши Моби Дика, примерно


297

Вот текстовый файл ASCII объемом 1,2 Мб, содержащий текст « Моби-Дика» Германа Мелвилла ; или Кит . Ваша задача состоит в том, чтобы написать программу или функцию (или класс и т. Д. - см. Ниже), которым будет присваиваться этот файл по одному символу за раз, и на каждом шаге должен угадываться следующий символ.

Это . Ваша оценка будет

2*L + E

где L- размер вашего представления в байтах, и Eэто количество символов, которое он угадывает неправильно. Самый низкий балл побеждает.

Дальнейшие подробности

Ваша заявка будет программой или функцией (и т. Д.), Которая будет вызываться или вызываться или отправлять данные несколько раз. (1215235 раз, чтобы быть точным.) Когда он вызывается в n- й раз, ему присваивается n- й символ whale.txtили, whale2.txtи он должен вывести свое предположение для ( n + 1 ) -го символа. EКомпонент его счет будет общее количество символов , которые он не угадает.

В большинстве представлений необходимо сохранять некоторое состояние между вызовами, чтобы они могли отслеживать, сколько раз они были вызваны и каковы были предыдущие входные данные. Вы можете сделать это, записав во внешний файл, используя staticили глобальные переменные, передав класс, а не функцию, используя монаду состояния или что-либо еще, что работает для вашего языка. Ваша заявка должна включать любой код, необходимый для инициализации его состояния перед первым вызовом.

Ваша программа должна выполняться детерминистически, так что она всегда делает одни и те же предположения при одинаковых входных данных (и, следовательно, всегда получает один и тот же результат).

Ваш ответ должен включать не только ваше представление, но и код, который вы использовали для расчета Eчасти его оценки. Это не обязательно должно быть написано на том же языке, что и ваше представление, и не будет учитываться при подсчете байтов. Вам предлагается сделать его читабельным.

Что касается интерфейса между вашей отправкой и этой программой для подсчета очков, все в порядке, если ваша программа всегда выдает один байт вывода до получения следующего байта ввода. (Так, например, вы не можете просто передать ему строку, содержащую все входные данные, и получить строку обратно, содержащую весь выходной.)

Вы должны запустить свою тестовую программу и рассчитать / проверить свой счет перед отправкой заявки. Если ваша заявка идет слишком медленно, чтобы вы могли проверить ее оценку, тогда она не может участвовать в соревнованиях, даже если вы знаете, какой будет ее оценка в принципе.

LКомпонент вашего счета будет рассчитываться в соответствии с обычными правилами для вызовов кода для гольфа. Если ваша заявка будет содержать несколько файлов, обратите внимание на правила оценки и структуру каталогов в этом случае. Любые данные, которые использует ваш код, должны быть включены в ваш Lсчет.

Вы можете импортировать существующие библиотеки, но не можете загружать любые другие внешние файлы, и ваш код может не получить доступ к whale.txtилиwhale2.txtФайл любым способом, кроме описанного выше. Вы не можете загружать предварительно обученные нейронные сети или другие источники статистических данных. (Можно использовать нейронные сети, но вы должны включить данные о весе в свое представление и сосчитать их в счетчике байтов.) Если по какой-то причине в вашем языке или библиотеках есть функция, предоставляющая весь или весь текст Moby Dick Вы не можете использовать эту функцию. Кроме того, вы можете использовать любые другие встроенные или библиотечные функции, которые вам нравятся, в том числе связанные с обработкой текста, предсказанием или сжатием, если они являются частью вашего языка или его стандартных библиотек. Для более экзотических, специализированных подпрограмм, которые включают источники статистических данных, вы должны будете реализовать их самостоятельно и включить в свой счетчик байтов.

Вполне вероятно, что некоторые материалы будут включать компоненты, которые сами генерируются кодом. Если это так, пожалуйста, включите в свой ответ код, который использовался для их создания, и объясните, как это работает . (Пока этот код не нужен для запуска вашего представления, он не будет включен в ваш счетчик байтов.)

По историческим причинам существует две версии файла, и вы можете использовать любую из них в ответе. В whale2.txt(ссылка выше) текст не переносится, поэтому новые строки появляются только в конце абзацев. В оригинале whale.txtтекст оборачивается на ширину в 74 символа, поэтому вы должны прогнозировать конец каждой строки, а также прогнозировать текст. Это делает задачу более трудной, поэтому whale2.txtрекомендуется для новых ответов. Оба файла имеют одинаковый размер, 1215236 байт.


Подводя итог, все ответы должны включать в себя следующее:

  • Ваша подача сама. (Код плюс любые файлы данных, которые он использует - это могут быть ссылки, если они большие.)
  • Объяснение того, как работает ваш код. Пожалуйста, объясните метод ввода / вывода, а также как он предсказывает следующий символ. Объяснение вашего алгоритма важно, и хорошие объяснения принесут мне награду.
  • Код, который вы использовали для оценки вашего счета. (Если это совпадает с предыдущим ответом, вы можете просто дать ссылку на него.)
  • Любой код, который вы использовали для создания вашего представления, вместе с объяснением этого кода. Это включает в себя код, который вы использовали для оптимизации параметров, создания файлов данных и т. Д. (Это не учитывается при подсчете байтов, но должно быть включено в ваш ответ).

Leaderboard

щедроты

Время от времени я буду предлагать награды, чтобы поощрять различные подходы.

Первый, 50 баллов, был присужден А. Рексу за лучший результат на тот момент.

Второе, 100 баллов, было также присуждено А. Рексу за тот же ответ, потому что они добавили очень хорошее объяснение к своему существующему ответу.

Следующая награда, 200 баллов , будет присуждена

  • Конкурентный ответ, который использует новую технику. (Это будет основано на моем субъективном суждении, поскольку в награду входит мой представитель, но вы можете доверять мне, чтобы быть справедливым. Обратите внимание, что ваш ответ должен содержать достаточно объяснений, чтобы я мог понять, как это работает!) Такой ответ не нужен Не берите наивысший балл, это просто необходимо сделать достаточно хорошо по сравнению с существующими ответами. Я особенно заинтересован в том, чтобы увидеть решения, основанные на рекуррентных нейронных сетях, но я буду награждать награду всем, что кажется достаточно отличным от марковских моделей, которые доминируют в текущих высших оценках.

Или же:

  • Любой, кто бьет лучший результат А. Рекса (в настоящее время 444444), используя любой метод.

После получения награды в 200 баллов я, скорее всего, предложу 400 баллов, соответственно обновив требования.


Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Деннис

9
xkcd.com/1960 является ссылкой на этот вызов!
А. Рекс

Я думал о сжатии этого ... но это слишком долго, что мой компьютер
сломался пожав

Ответы:



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

Он состоит из простой модели PPM, которая просматривает последние 8 символов, чтобы предсказать следующий.

Мы доверяем шаблону длины L, когда встречаемся с ним по крайней мере T [L] раз, где T - массив произвольных порогов: [1,1,2,1,2,3,5,2] . Кроме того, мы всегда доверяем шаблону, чей первый символ совпадает [A-Z '"(].

Мы выбираем самый длинный доверенный шаблон и возвращаем прогноз с наивысшей оценкой, связанной с этим шаблоном во время вызова.

Примечания

  • Это, очевидно, не оптимизировано для скорости, но на моем ноутбуке оно работает примерно за 15 секунд.

  • Если бы нам было позволено повторить процесс несколько раз подряд без сброса модели, число ошибок сравнялось бы с ~ 268000 после 5 итераций.

  • Текущий показатель успеха функции прогнозирования составляет ~ 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 - сэкономил 19644 балла, переключившись на whale2.txt (обновление задачи)
  • 544371 - сэкономил 327 баллов, заставив шаблоны, начинающиеся с заглавной буквы, кавычки, двойной кавычки или открывающей скобки, также всегда доверять
  • 544698 - сэкономил 2119 баллов, заставив шаблоны, начинающиеся с пробела, всегда доверять
  • 546817 - сэкономил 47 баллов, настроив пороги и используя функцию прогнозирования
  • 546864 - сохранено 1496 баллов за счет увеличения максимальной длины шаблона до 8 символов
  • 548360 - сэкономил 6239 баллов, введя понятие доверенных шаблонов с пороговыми значениями в зависимости от их длины
  • 554599 - сэкономлено 1030 баллов за счет улучшения прогноза перевода строки
  • 555629 - сэкономил 22 балла, сыграв в гольф функцию прогнозирования
  • 555651 - сэкономил 40 очков благодаря функции прогнозирования
  • 555691 - начальная оценка

44
Для любопытных, нет, это не производит ничего, как Моби Дик. Это очень много 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.
immibis

23
@immibis Название конкурса было выбрано с умом. Это Моби Дик, примерно . :-)
Арнаулд

3
@ Натаниэль Было много обновлений, так что это было бы едва читаемым и не очень информативным. Вместо этого я добавил журнал изменений с краткими пояснениями об улучшениях.
Арно

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. (Вы можете изменить это имя файла во втором экземпляре символа Bвыше.) Ниже описано, как создать этот файл.

Программа использует комбинацию моделей Маркова в основном как в этом ответе пользователя 2699 , но с несколькими небольшими модификациями. Это создает распределение для следующего символа. Мы используем теорию информации, чтобы решить, принимать ли ошибку или тратить биты памяти на Bподсказки кодирования (и если да, то как). Мы используем арифметическое кодирование для оптимального хранения дробных битов из модели.

Длина программы составляет 582 байта (включая ненужный заключительный Bсимвол новой строки), а длина двоичного файла составляет 69942 байта, поэтому по правилам оценки нескольких файлов мы получаем L582 + 69942 + 1 = 70525.

Программа почти наверняка требует 64-битной (little-endian?) Архитектуры. Для запуска m5.largeэкземпляра на Amazon 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, но это можно легко изменить во второй строке.

Сравнение текста

"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. Обратите внимание, что желаемым результатом является распределение , а не просто предположение для наиболее вероятного символа.

В нашем случае мы по существу используем модель в этом ответе пользователя 2699 . Мы не использовали модель из ответа с наивысшим баллом (кроме нашего) Андерса Касеорга именно потому, что нам не удалось извлечь распределение, а не единственное лучшее предположение. Теоретически, этот ответ вычисляет средневзвешенное геометрическое значение, но мы получили несколько плохие результаты, когда интерпретировали это слишком буквально. Мы «украли» модель из другого ответа, потому что наш «секретный соус» - не модель, а общий подход. Если у кого-то есть «лучшая» модель, тогда он сможет добиться лучших результатов, используя остальные наши методы.

Как примечание, большинство методов сжатия, таких как Lempel-Ziv, можно рассматривать как «модель языка», хотя, возможно, придется немного щуриться. (Это особенно сложно для того, что выполняет преобразование Берроуза-Уилера!) Также обратите внимание, что модель user2699 является модификацией модели Маркова; по сути, ничто иное не является конкурентоспособным для этой задачи или, возможно, даже для моделирования текста в целом.

Общая архитектура

В целях понимания полезно разбить общую архитектуру на несколько частей. С точки зрения самого высокого уровня, должен быть небольшой код управления состоянием. Это не особенно интересно, но для полноты картины мы хотим подчеркнуть, что в каждой точке программы запрашивается следующая догадка, в ней есть правильный префикс Moby Dick. Мы никоим образом не используем наши прошлые неверные догадки. Ради эффективности, языковая модель, вероятно, может повторно использовать свое состояние из первых N символов для вычисления своего состояния для первых (N + 1) символов, но в принципе она может пересчитывать вещи с нуля каждый раз, когда она вызывается.

Давайте отложим этот основной «драйвер» программы и заглянем внутрь части, которая угадывает следующий символ. Концептуально это помогает разделить три части: языковую модель (обсуждаемую выше), файл «подсказок» и «интерпретатор». На каждом шаге переводчик будет запрашивать у языковой модели распределение следующего символа и, возможно, считывать некоторую информацию из файла подсказок. Тогда он объединит эти части в догадку. Точно какая информация содержится в файле подсказок, а также как она используется, будет объяснено позже, но пока она помогает мысленно разделить эти части. Обратите внимание, что в плане реализации файл подсказок является буквально отдельным (двоичным) файлом, но он мог быть строкой или чем-то другим, хранящимся внутри программы. В качестве приближения

Если используется стандартный метод сжатия, такой как bzip2, как в этом ответе , файл «подсказок» соответствует сжатому файлу. «Интерпретатор» соответствует декомпрессору, в то время как «языковая модель» немного неявна (как упомянуто выше).

Зачем использовать файл подсказки?

Давайте выберем простой пример для дальнейшего анализа. Предположим, что текст состоит из Nдлинных символов и хорошо аппроксимируется моделью, в которой каждый символ (независимо) представляет собой букву Eс вероятностью чуть меньше половины, Tаналогично с вероятностью чуть меньше половины и Aс вероятностью 1/1000 = 0,1%. Давайте предположим, что другие символы невозможны; в любом случае, Aэто очень похоже на случай ранее невидимого символа на ровном месте.

Если мы работаем в режиме L 0 (как большинство, но не все другие ответы на этот вопрос), нет лучшей стратегии для переводчика, чем выбрать один из Eи T. В среднем примерно половина символов будет правильной. Так что E ≈ N / 2 и оценка ≈ N / 2 тоже. Однако, если мы используем стратегию сжатия, мы можем сжать до чуть более одного бита на символ. Поскольку L считается в байтах, мы получаем L ≈ N / 8 и, таким образом, получаем значение ≈ N / 4, что вдвое больше, чем в предыдущей стратегии.

Достижение этой скорости чуть более одного бита на символ для этой модели немного нетривиально, но одним из методов является арифметическое кодирование.

Арифметическое кодирование

Как известно, кодирование - это способ представления некоторых данных с использованием битов / байтов. Например, ASCII - это 7-битная / символьная кодировка английского текста и связанных с ним символов, и это кодировка исходного файла Moby Dick, который мы рассматриваем. Если некоторые буквы встречаются чаще, чем другие, то кодирование с фиксированной шириной, например ASCII, не является оптимальным. В такой ситуации многие люди тянутся к кодированию Хаффмана . Это оптимально, если вам нужен фиксированный (без префикса) код с целым числом битов на символ.

Однако арифметическое кодирование еще лучше. Грубо говоря, он может использовать «дробные» биты для кодирования информации. В Интернете доступно множество руководств по арифметическому кодированию. Здесь мы пропустим детали (особенно практической реализации, которая может быть немного хитрой с точки зрения программирования) из-за других ресурсов, доступных онлайн, но если кто-то пожалуется, возможно, этот раздел может быть расширен более подробно.

Если у вас есть текст, фактически сгенерированный известной языковой моделью, то арифметическое кодирование обеспечивает по существу оптимальное кодирование текста из этой модели. В некотором смысле это «решает» проблему сжатия для этой модели. (Таким образом, на практике основная проблема заключается в том, что модель неизвестна, и некоторые модели лучше, чем другие, моделируют человеческий текст.) Если в этом конкурсе не было допущено ошибок, то на языке предыдущего раздела Одним из способов решения этой проблемы было бы использование арифметического кодера для генерации файла «подсказок» из языковой модели, а затем использование арифметического декодера в качестве «интерпретатора».

В этом по существу оптимальном кодировании мы в конечном итоге тратим -log_2 (p) битов на символ с вероятностью p, а общая скорость кодирования - энтропия Шеннона . Это означает, что для символа с вероятностью, близкой к 1/2, требуется около одного бита для кодирования, а для символа с вероятностью 1/1000 - около 10 битов (поскольку 2 ^ 10 составляет примерно 1000).

Но показатель выигрыша для этой задачи был выбран правильно, чтобы избежать сжатия в качестве оптимальной стратегии. Мы должны найти способ сделать несколько ошибок в качестве компромисса для получения более короткого файла подсказок. Например, одна стратегия, которую можно попробовать, - это простая стратегия ветвления: мы обычно пытаемся использовать арифметическое кодирование, когда можем, но если распределение вероятностей из модели «плохое», мы просто угадываем наиболее вероятный символ и не не пытайтесь его кодировать.

Зачем делать ошибки?

Давайте проанализируем предыдущий пример, чтобы мотивировать, почему мы можем захотеть делать ошибки «намеренно». Если мы используем арифметическое кодирование для кодирования правильного символа, мы потратим примерно один бит в случае с Eили T, но около десяти бит в случае с A.

В целом, это довольно хорошая кодировка, тратящая чуть больше на символ, хотя есть три возможности; в принципе, Aэто маловероятно, и мы не заканчиваем тратить соответствующие десять бит слишком часто. Однако разве не было бы неплохо, если бы мы могли просто сделать ошибку вместо случая A? В конце концов, метрика для проблемы считает, что 1 байт = 8 бит длины эквивалентен 2 ошибкам; таким образом, кажется, что следует предпочесть ошибку, а не тратить более 8/2 = 4 бита на символ. Тратить больше байта на сохранение одной ошибки определенно звучит неоптимально!

Механизм «перемотки»

В этом разделе описывается основной умный аспект этого решения, который позволяет обрабатывать неправильные догадки без затрат на длину.

Для простого примера, который мы анализировали, механизм перемотки особенно прост. Интерпретатор читает один бит из файла подсказок. Если это 0, он угадывает E. Если это 1, он угадает T. В следующий раз, когда он вызывается, он видит правильный символ. Если файл подсказки настроен правильно, мы можем убедиться, что в случае Eили Tинтерпретатор правильно угадывает. Но как насчет A? Идея механизма перемотки заключается в том, чтобы просто не кодировать Aвообще . Точнее, если интерпретатор позже узнает, что правильный символ был A, он метафорически « перематывает ленту»: он возвращает бит, который он прочитал ранее. Бит, который он читает, намеревается закодировать EилиT, но не сейчас; это будет использовано позже. В этом простом примере это в основном означает, что он продолжает угадывать один и тот же символ ( Eили T) до тех пор, пока не получит его правильно; затем он читает еще один бит и продолжает идти.

Кодировка для этого файла подсказок очень проста: превратить все Es в 0 бит и Ts в 1 бит, полностью игнорируя As. В результате анализа в конце предыдущего раздела эта схема допускает некоторые ошибки, но снижает общий балл, не кодируя ни одну из As. В меньшем эффекте, он на самом деле экономит на длину намеков файла , а также, потому что мы в конечном итоге , используя ровно один бит для каждого Eи T, вместо того , чтобы немного больше , чем немного.

Маленькая теорема

Как мы решаем, когда сделать ошибку? Предположим, что наша модель дает нам распределение вероятности P для следующего символа. Мы разделим возможные символы на два класса: кодированные и не кодированные . Если правильный символ не закодирован, то мы в конечном итоге будем использовать механизм «перемотки», чтобы принять ошибку бесплатно. Если правильный символ закодирован, то мы будем использовать какой-то другой дистрибутив 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. Добавьте правильный символ к известному правильному префиксу Моби Дика.
  2. Обновите (марковскую) модель текста.
  3. Секретный соус : Если предыдущее предположение было неверным, перемотать состояние арифметического декодера его состояние перед предыдущей догадкой!
  4. Попросите модель Маркова предсказать распределение вероятности P для следующего символа.
  5. Преобразуйте P в Q, используя подпрограмму из предыдущего раздела.
  6. Попросите арифметический декодер декодировать символ из оставшейся части файла подсказок в соответствии с распределением Q.
  7. Угадай получившегося персонажа.

Кодировка файла подсказок работает аналогично. В этом случае программа знает, какой правильный следующий символ. Если это символ, который должен быть закодирован, тогда, конечно, следует использовать арифметический кодер на нем; но если это не кодированный символ, он просто не обновляет состояние арифметического кодировщика.

Если вы понимаете теоретико-информационный фон, такой как распределение вероятностей, энтропия, сжатие и арифметическое кодирование, но пытались и не смогли понять этот пост (за исключением того, почему теорема верна), дайте нам знать, и мы можем попытаться прояснить ситуацию. Спасибо за чтение!


8
Вау, впечатляющий ответ. Я предполагаю, что есть дополнительный код, необходимый для создания Bфайла? Если да, то можете ли вы включить это в свой ответ?
Натаниэль

8
Отлично! Первый (и пока единственный) ответ преодолеть барьер в 500 тысяч очков.
ShreevatsaR

5
"Краткая информация о китовом ките" Боже мой, я плачу
Филл

5
Поскольку в течение периода вознаграждения не было опубликовано ни одного нового ответа, я награждаю его вашим ответом как за лучший результат, так и за самый сложный подход. Если у вас есть время, я был бы очень признателен за более подробное объяснение того, как работает этот ответ, то есть каков именно алгоритм?
Натаниэль

2
@Nathaniel: я добавил объяснение в этот пост. Дайте мне знать, если вы думаете, что это достаточно подробно, чтобы воспроизвести решение самостоятельно.
А. Рекс

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 представление весов, где небольшие непечатаемые значения экранируются в восьмеричном виде.
Cœur

Я обновил вопрос, указав версию файла, в которой текст не обернут - вы можете попытаться повторно запустить свой код на этом (это может быть немного лучше)
Натаниэль

3
Спасибо за это объяснение - если бы вы могли отредактировать резюме в вопросе, было бы здорово. (Опыт с моей предыдущей задачей Paint Starry Night заключается в том, что эти процедуры оптимизации являются наиболее интересной частью ответов, поэтому гораздо лучше, если ответы содержат код, используемый для этого, и объяснение этого. Я включил правило в оба Испытывает, говоря, что они должны.)
Натаниэль

1
@Christoph Моя комбинация моделей на самом деле представляет собой взвешенное геометрическое среднее. Но среднее значение PAQ в области логистики немного отличается - я должен посмотреть, будет ли это лучше.
Андерс Касеорг

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

Попробуйте онлайн!

Функция, использующая глобальные переменные. Изучая, как это происходит, выстраивая модель на уровне слова: учитывая то, что он видел в этом слове , какой персонаж чаще всего встречается? По мере того, как вводится больше информации, он довольно хорошо изучает обычные слова из текста, а также изучает наиболее распространенный символ для начала следующего слова.

Например:

  • Если то, что он видел до сих пор, это «Captai», он предсказывает «n»
  • Если это «Капитан», он предсказывает пробел
  • Если это начало слова, а последним словом было «Капитан», оно предсказывает «А»
  • Если до сих пор слово «A», оно предсказывает «h» (а затем «a» и «b»; аналогично для «C»).

В начале это не очень хорошо, но к концу появляются большие части реальных слов. Резервный вариант - это пробел, а после единственного пробела это «а», если предыдущей буквой не было «nedtfo», цифры, дефиса или апострофа. Он также агрессивно предсказывает разрывы строк после 71 символа, или если после 66 ожидался пробел. Оба из них были только настроены на данные («t» гораздо чаще встречается после пробела, но чаще уже предсказывалось, поэтому » «это лучшее предположение за пределами этих шести особых случаев).

Изучение того, какие пары слов были вместе, и предпосевное отображение оказались бесполезными.


В итоге получается такой текст:

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!

что соответствует этой части ввода:

все вокруг него.

«Я видел его почти в то же мгновение, сэр, как и капитан Ахав, и я закричал», - сказал Таштего.

«Не в одно мгновение; не в то же самое - нет, дублон мой, судьба зарезервировала для меня дублон. Только я; никто из вас не мог поднять Белого кита первым. Там она дует! - там она дует! - - вот она и дует!

Вы можете видеть, где, в частности, правильные существительные получаются довольно хорошо, но концы слов в основном правильные. Когда он видит «доу», он ожидает «сомнения», но как только появляется «я», он получает «дублон».

Если вы запускаете его второй раз с той же моделью, которую он только что построил, он сразу же получает правильные 92 КБ (51,7% -> 59,3%), но это всегда чуть меньше 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

Спасибо @Quentin за сохранение 217 байтов!

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чтобы сохранить группу символов :)
Квентин,

@Quentin Спасибо! Теперь мне интересно, почему я об этом не подумал ...
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')]

Результаты:

В основном тарабарщина, хотя вы можете заметить, что она появляется на случайной фразе, такой как «Отец Мэппл».

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

С (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-символьную последовательность, и возвращает это значение. В некоторой степени аналогично подходу Арно, описанному выше, но полагается на присущую ему вероятность двух заданных последовательностей из 4 символов, заканчивающихся одинаково.

Де-golfed:

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 Я добавил версию для игры в гольф (которую я сделал, потому что я тоже не мог следить за ней) - надеюсь, это вас не беспокоит, но, пожалуйста, откатитесь, если хотите.
Адам Дэвис

@AdamDavis в большинстве реализаций C, все глобальные переменные начинаются с нуля. Это неопределенное поведение, поэтому оно используется только в код-гольфе.
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 whale2.txt с пропущенным первым байтом

Игнорирует его ввод; выводит правильный ответ. Это обеспечивает базовую линию на одном конце; Даниеро обеспечивает базовую линию на другом конце.

Скрипт строителя:

#!/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). Этот тестовый комплект может быть использован любым пользователем на подходящей платформе, которая представляет полную программу, которая ожидает ввода / вывода для чтения / записи. Он использует байтовый ввод-вывод, чтобы избежать мошенничества. Дочерняя программа должна сбрасывать вывод после каждого байта, чтобы избежать блокировки.

#!/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.txtили, whale2.txtи она должна вывести свое предположение для (n+1)thхарактер." - Как выполняется это требование? Код отображает весь текст whale.txtкаждый раз, когда он выполняется.
Аксиак

1
@axiac "все в порядке, если ваша программа всегда выдает один байт вывода до получения следующего байта ввода."
user202729

5
@axiac, учитывая тестовую проводку, я с радостью расцениваю посылку программы в один байт из STDIN как "вызов или вызов". Суть в том, что программа возвращает один байт вывода после каждого байта ввода, что на самом деле делает этот, когда проходит через тестовый жгут. Как говорится в вопросе, «все в порядке, если ваша программа всегда выдает один байт вывода до получения следующего байта ввода».
Натаниэль

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 секунды. Для справки: вот что я получаю, когда меняю размер ключей таблицы соответствия (включая результаты второго запуска без сброса модели):

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
Спасибо за показ отклонений при изменении размера ключа и порога столбца!
Джереми Вейрих

Я обновил вопрос с помощью версии файла, в которой текст не обернут - вы, возможно, сможете сэкономить некоторые моменты, используя это
Натаниэль

@ Натаниэль это действительно так. Я обновил ответ.
Чарли

Вы можете сохранить несколько байтов, используя var вместо объявления типов.
Ed T

1
По мере увеличения размера ключа количество пропущенных попаданий будет уменьшаться, и, следовательно, будет выводиться больше пробелов, когда более короткая клавиша могла угадать правильный символ. Поскольку размер ключа становится меньше, отдельные догадки становятся менее точными для соответствующих сегментов. Я подозреваю, что именно поэтому длина четырех оптимальна. Если вы сохранили ключи различной длины и использовали более короткие совпадения, когда более длинные не доступны, то я ожидаю, что частота попаданий (и, следовательно, оценка) будет значительно улучшена при более длинных ключах.
Джеффри Л. Уитледж

11

Java 7, 1995 символов, (1995 * 2 + 525158) 529148

Java - отстой для небольших программ. Во всяком случае, я попробовал несколько чрезвычайно сложных и хитрых подходов, которые дали удивительно дерьмовые результаты. Впоследствии я вернулся и просто применил простой подход, который привел к уменьшению размера программы и лучшим результатам.

Этот подход на самом деле очень прост. Он вслепую подает предыдущие символы x (в дополнение ко всем подстрокам этих символов) в хеш-таблицу, сопоставленную с текущим символом. Затем он отслеживает, какие шаблоны наиболее точно предсказывают текущий символ. Если шаблоны, которые предшествуют определенным символам, встречаются несколько раз, они преуспевают в предсказании символа. Он дает приоритет более длинным строкам и приоритет тому, какой символ чаще всего следует за данной строкой. Этот алгоритм ничего не знает о типе документа или английском языке.

Я остановился на использовании 9 символов и попытался сопоставить целые слова в этих предыдущих 9 символах, когда это возможно. Если вы не пытаетесь сопоставлять слова в строках, оптимальная длина составляет 6 символов, что приводит к еще нескольким тысячам неправильных предсказаний.

Одним интересным наблюдением было то, что использование 20 символов в первый раз приводило к ошибочным прогнозам, но при последующих проходах точность составляла 99,9%. Алгоритм был в основном способен запоминать книгу в перекрывающихся 20-байтовых кусках, и это было достаточно отчетливо, чтобы позволить ему вспомнить всю книгу за символ за раз.

  • (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
Я подумал, что это стоило того, так как размер файла дает много возможностей для улучшения по сравнению с размером программы.
Джим В.

3
Это не компилируется под Java 7 (или любой другой версией Java, если она того стоит). Не могли бы вы исправить свой код? Как только это будет сделано, я с удовольствием сыграю в гольф, чтобы ваш счет улучшился.
Оливье Грегуар

Непроверенный, но это должен быть точно такой же код, слегка измененный: 950 байт . Ваш текущий код содержал довольно много ошибок, поэтому я не уверен, правильно ли я все заполнил. Опять же, не проверено, поэтому просто сравните версии, чтобы увидеть, что я изменил / переименовал, и посмотреть, все ли работает так же, как ваш исходный код. Хотя определенно можно сыграть в гольф еще.
Кевин Круйссен

Черт, я делал это, когда мне было скучно на моей старой работе, и я не брал код с собой. Я должен взглянуть на это, чтобы увидеть, где опечатка.
Джим Ш

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с учетом регистра и включая знаки препинания). Затем он ищет в этом словаре слова, начинающиеся с того, что ему до сих пор известно о текущем слове, сортирует результирующий список по частоте встречаемости (медленно) и догадывается, что следующий символ - следующий символ в наиболее распространенном совпадающем слове. Если у нас уже есть наиболее распространенное совпадающее слово или более не совпадающее слово существует, оно возвращается .

Это также создает отвратительно неэффективный словарь пар слов. При достижении границы слова он предполагает, что следующим символом является первая буква второго слова в наиболее часто встречающейся паре слов или tесли совпадения нет. Это не очень умно, хотя. После Mobyэтого программа правильно угадывает, что следующий символ - это следующий D, но затем она забывает все о контексте и обычно заканчивает тем, что называет кита «Moby Duck» (потому что слово «голландский», кажется, встречается чаще в первой половине текста ). Это было бы легко исправить, расставив приоритеты между парами слов над отдельными словами, но я ожидаю, что выигрыш будет незначительным (поскольку обычно он корректен с третьего символа, а пары слов не очень полезны в первую очередь).

Я мог бы настроить это, чтобы лучше соответствовать предоставленному тексту, но я не думаю, что ручная настройка алгоритма, основанная на предварительных знаниях ввода, действительно соответствует духу игры, за исключением выбора t в качестве запасного символа после пробела ( и я, вероятно, не должен был этого делать), я избежал этого. Я проигнорировал известную длину строки входного файла и вместо этого вставил \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 "

Это сделало бы Гнев Хана намного более запутанным. И «одинокий» → «покалывание» является особенно удовлетворительной заменой.

Изменить: один байт сохранен путем удаления постороннего пространства

счет

#! /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 и выводит «прогнозируемый» текст на стандартный вывод, а также злоупотребляет stderr для записи счета. Я бы порекомендовал перенаправить вывод в файл.


2
Добро пожаловать в PPCG!
Мартин Эндер

1
Не lambda i:i[1]будет дешевле, чем иметь дело с operator?
Драконис

@Draconis Почти наверняка.
Джорджу Уотсон,

9

С ++, 2 · 62829 + 318786 = 444444

Чтобы запустить эту программу, вам нужен этот файл , который должен быть назван C.

В программе используется та же комбинация марковских моделей, что и в нашем предыдущем ответе . Как и прежде, эта комбинация по сути является моделью из этого ответа пользователя 2699 , но с небольшими изменениями.

Видя, как в этом ответе используется та же самая модель, что и раньше, улучшение является лучшим теоретико-информационным механизмом, чем механизм «перемотки», описанный ранее. Это позволяет делать меньше ошибок, а также иметь меньшую общую длину. Сама программа не очень удачна, потому что она не является основным источником оценки.

Длина программы составляет 2167 байт (включая все вкладки для отступа и множество других ненужных символов, но перед тестовым кодом), а Cдлина двоичного файла составляет 60661 байт, поэтому по правилам оценки нескольких файлов мы Lоцениваем как 2167 + 60661 + 1 = 62829.

Программа занимает приблизительно 8 минут для запуска на m5.4xlargeэкземпляре в Amazon EC2 и использует чуть более 16 ГБ памяти. (Это чрезмерное использование памяти не является необходимым - мы просто не оптимизировали это либо.)

#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 является наиболее частым следующим символом, следующим за этим шаблоном. Таким образом, мы собираем до девяти догадок. Чтобы решить, какое предположение использовать, мы взвешиваем частоту каждого паттерна по длине до 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))

Вот пример выходных данных в начале текста. Уже сейчас мы начинаем видеть возможность закончить общие слова после просмотра их первого письма ( in, to, and, by, а также, по- видимому, 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- для sailor, я полагаю. Или, опять же, после , aтого , как он ожидает - nвозможно, из-за обычного возникновения , and.


Changelog:

  • 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

С ++ (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, а строка abcdefghiи abcdefghjпоявляется с наибольшей частотой во всех строках формы abcdefgh*, на выходе будет либо iили j, тай - брейке с более короткими суффиксами ( bcdefgh, cdefgh, ...).

По неизвестным причинам, больше чем 7, и мой компьютер не имеет достаточно оперативной памяти для его запуска. Даже с 7 мне нужно закрыть все веб-браузеры, чтобы запустить его.


Тестовый код:

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

Пример вывода:

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

Этот в конце текста. Большинство длинных слов предсказаны довольно точно ( 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

Прописные буквы не кажутся хорошими.


Кажется, что Trie потребляет больше памяти, чем я ожидал ...
user202729

... а также сложнее реализовать.
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"':' ',.......}, и aпредставляет собой словарь , который отображает от 2 символа 1 символ. В основном двухсимвольный вариант ответа Steadybox .
user202729

1
Как я вижу, литерал составляет 17780 байт. Вы можете уменьшить его до 11619 символов, удалив пробелы в распакованном содержимом, что экономит 12322 байта. (если я посчитал правильно) Также ... преобразование шестнадцатеричных escape-кодов в реальные необработанные символы может сэкономить еще больше байтов.
user202729

Как мне опубликовать что-то здесь, если это сырые байты?
Скайлер

1
xxd, hexdump, uuencode, Или аналогичные
Питер Тейлор

@ 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

Использует три предварительно вычисленных вспомогательных файла:

  • 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

С ++ (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 - включает в себя полный поиск программы и семян)

Это решение основано на ГСЧ. Сначала я анализирую текст. Я создаю карту, которая подсчитывает вхождения двух последовательных символов. Затем я создаю карту распространения. Все это делается статически, поэтому должно соответствовать правилам.

Затем, пытаясь напечатать текст, я делаю поиск и вытаскиваю случайный символ из возможных. Хотя обычно это приводит к худшим результатам, чем просто вывод наиболее распространенного следующего письма, вполне возможно, что божьи семена будут давать лучшие результаты. Вот почему семя жестко закодировано. В настоящее время я ищу лучшее семя. И я обновлю этот ответ, как только найду лучшие семена. Так что оставайтесь в курсе!

Если кто-то хочет найти семена самостоятельно или использовать разные ГСЧ, не стесняйтесь раскошелиться на репо.

Метод, используемый для подсчета очков: 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)

Не могли бы вы включить в свой ответ тестовую систему, которая оценивает результат?
Натаниэль

@ Натаниэль Я связал код счета напрямую. В дополнение к хранилищу вы считаете это достаточно?
BrainStone

Изучив правила, я заметил, что нарушаю некоторые из них. Я естественно обновлю свой ответ, как только я исправлю проблемы
BrainStone

Затем вы в конечном итоге закодируете текст в случайное начальное число. Посмотрите эзотерический язык программирования Seed , и вы можете захотеть перепроектировать программу MT19937 и пройти этот ответ (если можете).
user202729

Хорошая идея, но не поможет получить хороший результат. +1 в любом случае.
user202729

3

Рубин, 1164418 (ай)

Я просто хотел посмотреть, насколько хорошо я могу сделать, не проверяя другие ответы.
Я не уверен, разрешено ли это, потому что он включает в себя литерал, который я сгенерировал, анализируя файл, но даже если это не так, это не значит, что он может кого-то избить.

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

Я уверен, что это разрешено; если вы проанализировали файл, хорошая работа! Только если программа делает это недействительным.
Эрик Outgolfer

@EriktheOutgolfer> _> (молча вставляет «(неконкурентный)» в заголовок)
NO_BOOT_DEVICE

Почему? Если это действительно так, он конкурирует, хотя может и не сильно побить. Если оно недопустимо (то есть ваше решение читает файл, а не просто содержит литерал), его следует удалить.
Эрик Outgolfer

Хммм. Я думал, что вы имели в виду, если какая-либо программа проанализировала файл, а не только решение.
NO_BOOT_DEVICE

1
Я не могу читать Руби, но я думаю, что это действительно. Наличие литерала внутри программы совершенно нормально, это совсем не проблема.
Натаниэль

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 '###' в тексте ответа.

Это генерируется с помощью следующего кода, где «updated.py» - это сгенерированный файл, а «cheatsheet.txt» - файл 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]')

Код можно выполнить, добавив следующее в конец файла «ified.py ». «whale2.txt» должен находиться в том же каталоге, что и «ified.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, когда
считал

2
да, это была опечатка, которая размножалась
Легорхин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.