Напишіть Мобі Діку, приблизно


297

Ось 1.2Mb текстовий файл ASCII, що містить текст Мобі-Діка Германа Мелвілла ; або, Кит . Ваше завдання - написати програму або функцію (або клас тощо - див. Нижче), якому цей файл буде наданий по одному символу, і на кожному кроці повинен вгадувати наступний символ.

Це . Ваш рахунок буде

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 байт.


Підсумовуючи, всі відповіді повинні містити такі речі:

  • Само ваше подання. (Код, а також усі файли даних, які він використовує - це можуть бути посилання, якщо вони великі.)
  • Пояснення, як працює ваш код. Поясніть, будь ласка, метод вводу / виводу, а також те, як він передбачає наступний символ. Пояснення вашого алгоритму важливе, і хороші пояснення зароблять від мене щедрості.
  • Код, який ви використовували для оцінки своєї оцінки. (Якщо це ідентично попередній відповіді, ви можете просто посилання на нього.)
  • Будь-який код, який ви використовували для створення подання, а також пояснення цього коду. Сюди входить код, який ви використовували для оптимізації параметрів, генерації файлів даних і т.д.

Таблиця лідерів

Bounties

Час від часу я пропоную щедроти для заохочення різних підходів.

Перший, 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.
іммібіс

23
@immibis Назва вибору була обрана з розумом. Це приблизно Мобі Дік . :-)
Арнольд

3
@Nathaniel Там було багато оновлень, так що це було б ледь читабельним і не дуже інформативним. Натомість я додав журнал змін із короткими поясненнями щодо покращень.
Арнольд

45
Я думаю, що ваша програма насправді робить ідеальний переклад на гельську.
Беська

1
@ Draco18s Важко сказати, хороша чи погана здогадка ця кома. Якщо це було поганим здогадком, функція передбачення, можливо, легітимно намагалася поставити лист після того, як це було зроблено іншим буквою - що - було - насправді - там - замість - .
Арнольд

91

Perl, 2 · 70525 + 326508 = 467558

Прогноз

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

Щоб запустити цю програму, вам потрібен цей файл тут , який повинен бути названий B. (Ви можете змінити це ім'я файлу у другому екземплярі символу Bвище.) Див. Нижче, як створити цей файл.

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

Програма має 582 байти (включаючи непотрібний остаточний рядок), а двійковий файл B- 69942 байт, тому згідно з правилами скорингу декількох файлів ми Lоцінюємо як 582 + 69942 + 1 = 70525.

Програма майже напевно вимагає 64-бітової (мало-ендіанської?) Архітектури. Для запуску 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 . Ми не використали модель з відповіді найвищої оцінки (крім нашої власної) Андерса Касеорга саме тому, що нам не вдалося витягнути розподіл, а не єдину найкращу здогадку. Теоретично ця відповідь обчислює середньозважене геометричне значення, але ми отримали дещо погані результати, коли інтерпретували це занадто буквально. Ми "вкрали" модель з іншої відповіді, тому що наш "секретний соус" - це не модель, а загальний підхід. Якщо хтось має "кращу" модель, то він повинен мати можливість отримати кращі результати, використовуючи решту наших методик.

Як зауваження, більшість методів стиснення, такі як Лемпель-Зів, можна розглядати як "мовну модель" таким чином, хоча, можливо, доведеться трохи косити. (Це особливо хитро для чогось, що перетворює Берроуса-Уілера!) Також зауважте, що модель користувачем2699 є модифікацією моделі Маркова; по суті, ніщо інше не є конкурентоспроможним для цього завдання або, можливо, навіть моделювання тексту загалом.

Загальна архітектура

Для розуміння приємно розбити загальну архітектуру на кілька частин. З точки зору вищого рівня, має бути трохи коду управління державою. Це не особливо цікаво, але для повноти ми хочемо підкреслити, що в кожен момент програми запитують наступну здогадку, вона має у своєму розпорядженні правильний приставку Мобі Діка. Ми жодним чином не використовуємо наші помилкові здогадки. З метою ефективності мовна модель, ймовірно, може повторно використовувати свій стан з перших 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 біт, і все це Aповністю ігнорувати . Аналізуючи в кінці попереднього розділу, ця схема робить деякі помилки, але зменшує загальний бал, не кодуючи жодну з 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. Додайте правильний символ до відомого правильного префікса Moby Dick.
  2. Оновіть модель (Маркова) тексту.
  3. Секретний соус : Якщо попереднє припущення було невірним, перемотати стан арифметичного декодера його стан перед попередньої здогадкою!
  4. Попросіть модель Маркова передбачити розподіл ймовірностей P для наступного символу.
  5. Перетворіть P на Q, використовуючи підпрограму з попереднього розділу.
  6. Попросіть арифметичний декодер для декодування символу з залишку файлу підказки, відповідно до розподілу Q.
  7. Відгадайте отриманий персонаж.

Кодування файлу підказок працює аналогічно. У такому випадку програма знає, що є правильним наступним символом. Якщо це символ, який слід закодувати, то, звичайно, слід використовувати арифметичний кодер на ньому; але якщо це не кодований символ, він просто не оновлює стан арифметичного кодера.

Якщо ви розумієте інформаційно-теоретичну основу, як розподіл ймовірностей, ентропія, стиснення та арифметичне кодування, але намагалися та не зрозуміли цю посаду (за винятком того, чому теорема справжня), дайте нам знати, і ми можемо спробувати прояснити речі. Дякуємо за прочитане!


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

8
Відмінно! Перша (і поки що єдина) відповідь, щоб зламати бар'єрний бал 500k.
ShreevatsaR

5
Омг я плачу
Phill

5
Оскільки в період щедрості не було опубліковано жодних нових відповідей, я присуджую це вашій відповіді як за найкращий результат, так і за найскладніший підхід. Якщо у вас коли-небудь буде час, я б дуже вдячний за більш глибоке пояснення того, як працює ця відповідь, тобто який саме алгоритм?
Натаніел

2
@Nathaniel: Я додав пояснення до цієї публікації. Повідомте мене, якщо ви вважаєте, що це досить детально, щоб самостійно відтворити рішення.
А. Рекс

77

Пітон 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 ± 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

Пітон 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"
  • Якщо слово досі є "A", воно передбачає "h" (а потім "a" і "b"; аналогічно "C").

На початку це не дуже добре, але до кінця виходять великі частини фактичних слів. Резервний варіант - це пробіл, і після одного пробілу це "а", якщо тільки попередня літера не була "недтфо", цифрою або дефісом або апострофом. Він також агресивно прогнозує розрив рядків після 71 символу, або якщо пробіл очікувався після 66. Обидва з них були просто налаштовані на дані ("t" набагато частіше зустрічається після пробілу, але частіше вже передбачається, так " a "краще здогадатися поза цими шістьма особливими випадками".

Дізнатися про те, які пари слів пішли разом, і попередньо здійснити попереднє відображення виявилося не варто.


Це закінчується таким текстом:

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

що відповідає цій частині вводу:

все навколо нього.

"Я бачив його майже тієї ж миті, сер, що це зробив капітан Ахав, і я закричав", - сказав Таштего.

"Не той самий момент; не той самий - ні, сумнівний мій, доля зарезервувала для мене сумніву. Я лише; ніхто з вас не міг підняти Білого кита спочатку. Там вона дме! - Там вона дме! - -туть вона дме!

Ви можете бачити, де належні іменники, зокрема, виходять досить добре, але кінці слів, як правило, також є правильними. Коли його бачать "ду", він очікує "сумніву", але як тільки з'являється "l", він отримує "сумнівну".

Якщо ви запускаєте його вдруге з тією ж моделлю, щойно вбудований, він одразу отримує ще 92k правильність (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

Пітон, 2 * 516 + 521122 = 522154

Алгоритм:

Ще одне подання пітона, цей алгоритм обчислює найбільш ймовірний наступний лист, дивлячись на послідовності довжиною 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')]

Результати:

Переважно гнучки, хоча ви можете бачити, що це трапляється на випадковій фразі, наприклад, "Отче Mapple".

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

Код тесту:

Досить просто, виводить кілька прикладів тексту в різних точках. Використовує whale2.txt, оскільки це дозволяє уникнути додаткової логіки для обчислення нових рядків.

from minified import A

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

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

3
Ласкаво просимо на сайт! Це фантастичне перше подання. :)
DJMcMayhem

@DJMcMayhem, Дякую за привітання Я насолоджувався переглядом деякий час, це перший конкурс, який привернув мою увагу до участі.
користувач2699

19

C (gcc) , 679787 652892

84 76 байт, 679619 652740 невірні здогадки

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

Спробуйте в Інтернеті!

Оновлення: ~ 27000 балів з оновленим файлом, 16 балів (8 байт) з кращою функцією гольфу.

Пояснення

Це працює так: коли код проходить через текст, він запам'ятовує останній символ, який припинив будь-яку задану 4-символьну послідовність, і повертає це значення. Дещо схожий на підхід Арнольда вище, але покладається на притаманну ймовірність того, що дві задані 4-символьні послідовності закінчуються однаково.

Де-гольф:

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

... Посилання TIO марно. Отже функція повертає значення останнього призначення?
користувач202729

Дозвольте мені відредагувати відповідь з поясненням, тоді :)

1
@Rogem Я додав версію для де-гольфу (що я зробив, тому що я не міг її виконати) - сподіваємось, це вас не турбує, але, за бажанням, будь ласка, відкатуйтесь.
Адам Девіс

@AdamDavis у більшості реалізацій C всі глобальні змінні починаються з нуля. Це невизначена поведінка, тому використовується лише в коді-гольф.
NieDzejkob

1
@NieDzejkob Ах, ви праві, дякую! "ANSI-C вимагає, щоб усі неініціалізовані статичні / глобальні змінні ініціалізувалися з 0."
Адам Девіс

16

ш + bzip2, 2 * 364106 = 728212

2 * 381249 + 0 = 762498

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

Далі слід стиснутий bzip2 кит2.txt з відсутнім першим байтом

Ігнорує його вхід; виводить правильну відповідь. Це забезпечує базову лінію на одному кінці; daniero забезпечує базову лінію на іншому кінці.

Сценарій Builder:

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

Тестовий джгут вводу / виводу (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 Стислі дані ставляться після того, що тут показано, і до самого коду.
користувач202729

4
У запитанні сказано: "Ваша заявка буде програмою або функцією (тощо), яка буде викликана або викликана кілька разів. Коли вона буде викликана протягом nthчасу, їй буде наданий n-й символ whale.txtабо, whale2.txtі він повинен вивести свої здогадки для (n+1)thхарактер ". - Як виконується ця вимога? Код відображає весь текст whale.txtкожного разу, коли він виконується.
аксіак

1
@axiac "все добре, якщо ваша програма завжди дає один байт виводу, перш ніж отримувати наступний байт введення".
користувач202729

5
@axiac, з огляду на тест-ремені, я радий розглянути програму на один байт від STDIN як "виклик або виклик" його. Річ у імпорті полягає в тому, що програма повертає один байт виводу після кожного байта вводу, що це робить насправді, коли проходить через тестовий джгут. Як йдеться в запитанні, "все в порядку, поки ваша програма завжди дає один байт виводу, перш ніж отримувати наступний байт введення".
Натаніел

13

Пітон 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
Дякуємо, що показали дисперсію під час зміни розміру ключа та порогу стовпця!
Джеремі Вейріх

Я оновив питання з версією файлу, де текст не загортається - ви, певно, можете зберегти деякі моменти, скориставшись цим
Натаніел

@Nathaniel це справді. Я оновив відповідь.
Чарлі

Ви можете зберегти кілька байтів, використовуючи var, замість декларування типів.
Ред 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
Я подумав, що варто зняти, оскільки розмір файлу дає багато можливостей для вдосконалення порівняно з розміром програми.
Jim W

3
Це не компілюється під Java 7 (або будь-яка версія Java, для чого це варто). Ви хочете виправити свій код? Як тільки це буде зроблено, я з радістю гольфу, щоб ваш результат був покращений.
Олів'є Грегоар

Неперевірено, але це має бути ваш точно такий же код злегка гольф: 950 байт . Ваш поточний код містив досить багато помилок, тому я не впевнений, чи правильно я все заповнив. Знову ж таки, неперевірений, тому просто порівняйте версії, щоб побачити, що я змінив / перейменував, і побачите, чи все ще працює так, як ваш оригінальний код. Але, безумовно, можна пограти ще трохи.
Кевін Круїссен

Лайно, я це робив, коли нудьгував на своїй старій роботі і не брав з собою код. Мені доведеться поглянути на це, щоб побачити, де друкарня.
Jim W

10

Пітон 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 Майже напевно.
georgewatson

9

C ++, 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

Пітон 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.


Журнал змін:

  • 274 * 2 + 526092 = 526640 Гольф алгоритм, ціною кількох додаткових помилок
  • 306 * 2 + 526089 = 526701 Оригінальна версія

6

Python 2, оцінка: 2 * (407 + 56574) + 562262 = 676224

Пошук слів, що відповідають попереднім символам, зі списку  всіх  більшості слів, використаних у тексті, відсортований за кількістю їх виникнення.

Код:

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

Дані: https://www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0

Тестовий набір:

incorrect = 0

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

print incorrect

Правка: Використання whale2.txtдає кращу оцінку.


5

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

Ще одне подання префікса за частотою. Бігайте далі whale2.txtі отримуйте схожий (трохи гірший) бал, ніж інші.

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

Цей жадібно знайде найдовший рядок, який починається з суфіксу історії, і якщо є кілька кандидатів, краватка з коротшими рядками.

Наприклад: Якщо останні 7 символи abcdefgh, а рядок 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';
}

Безголовки:

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

Верхній регістр не здається гарним.


Триє здається більш
зайнятим

... а також складніше здійснити.
користувач202729

4

Пітон 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 символу. В основному 2-символьний варіант Steadybox в відповідь .
користувач202729

1
Як я бачу, буквальний - 17780 байт. Ви можете зменшити його до 11619 символів, видаливши пробіли в декомпресованому вмісті, що заощаджує 12322 байти. (якщо я правильно порахував) Також ... перетворення шістнадцяткових кодів виходу в фактичні необроблені символи може заощадити ще більше байтів.
користувач202729

Як я щось тут розміщую, якщо це сирі байти?
Скайлер

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

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

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

using namespace std;

constexpr minstd_rand::result_type seed = 10087702;

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

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

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

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

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

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

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

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

    minstd_rand rng;

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

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

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

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

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

    generator2() :
            rng(seed) {
    }

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

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

На даний момент це рішення є WIP і, отже, не гольф. З огляду на те, що фактичний розмір коду ледь не впливає на результат, я думав, що спочатку розміщую свою відповідь, перш ніж почати мікрооптимізувати її.
(Повний код доступний тут: https://github.com/BrainStone/MobyDickRNG - включає повний пошук програми та пошук насіння)

Це рішення засноване на RNG. Спочатку аналізую текст. Я створюю карту, яка рахує події двох послідовних символів. Потім я створюю карту розподілу. Це все робиться статично, так має бути відповідно до правил.

Потім, намагаючись роздрукувати текст, я роблю пошук і витягую випадковий символ можливих. Хоча це, як правило, дає гірші результати, ніж просто виведення найпоширенішого наступного листа, але ймовірно, насіння бога дасть кращі результати. Ось чому насіння важко закодовано. Зараз я шукаю найкраще насіння. І я оновлю цю відповідь, як тільки знайду кращі насіння. Тож залишайтеся в курсі!

Якщо хтось хоче самостійно шукати насіння або використовувати різні РНГ, сміливо розкачайте репо.

Метод, що використовується для підрахунку балів: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15

Зауважте, що хоча загальний бал є найгіршим на даний момент, він б'є кількість помилок лише пробілів. І шанси хороші, що оцінка знизиться, перевіривши більше насіння.

Журнал змін

  • 24.01.2018 : Опублікована інітальна відповідь.
    Перевірене насіння: 0-50000. Оцінка: 2305 * 2 + 1017754 = 1022364
  • 24.01.2018 : Зробив мінімальний гольф. Додано посилання на метод підрахунку балів.
    Перевірене насіння: 0-80000. Оцінка: 1920 * 2 + 1017754 = 1021594 (-770)
  • 2018/02/02 : Нове насіння (10087702) (не знайшли часу, щоб виправити подання)
    Перевірено насіння: 0-32000000. Оцінка: 1923 * 2 + 1017344 = 1021190 (-404)

Чи можете ви включити у свою відповідь тестову запряжку, яка оцінює бал?
Натаніел

@Nathaniel Я безпосередньо зв'язав код рахунку. Окрім сховища, чи вважаєте ви цього достатньо?
BrainStone

Переглянувши правила, я помітив, що я порушую деякі з них. Я, звичайно, оновлю свою відповідь, як тільки виправлю проблеми
BrainStone

Потім ви закінчите кодування тексту до випадкового насіння. Див. Езотеричну мову програмування Seed , і ви, можливо, захочете змінити програму MT19937 і перемогти цю відповідь (якщо можете).
користувач202729

Хороша ідея, але не допоможе отримати хороший бал. +1 у будь-якому випадку
користувач202729

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

Я впевнений, що це дозволено; якщо ви проаналізували файл, хороша робота! Тільки якщо програма це робить, це недійсне.
Ерік Аутгольфер

@EriktheOutgolfer> _> (мовчки ковзає "(не змагається)" у назву)
NO_BOOT_DEVICE

Чому? Якщо це дійсно, він конкурує, навіть не може сильно бити. Якщо він недійсний (тобто ваше рішення зчитується з файлу, а не містить просто літералу), його слід видалити.
Ерік Аутгольфер

Хммм. Я думав, ти маєш на увазі, якщо якась програма аналізує файл, а не лише рішення.
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', найнижчий символ у файлі), і символ у кожному індексі є найчастішим наступним символом. Припускаючи, що я обчислив частоти правильно ...

Тестовано з кодом з тесту користувача202729


Чи можете ви зберегти кілька байт за допомогою def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]?
Ніл

0

[Python 3] (644449 * 2 + 0) 1288898 балів

Ідеальна точність всього в 644449 байтах

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

Повний код не може вписатись у відповідь, тому я його виклав тут і замінив великий двійковий літеральний рядок на b '###' у тексті відповіді.

Це генерується за допомогою наступного коду, де "modified.py" є згенерованим файлом, а "cheatsheet.txt" - файлом 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]')

Код можна виконати, додавши наступне до кінця "модифікованого.py". "whale2.txt" повинен знаходитися в тій самій каталозі, що і "modified.py", а вихід буде записаний у "out.txt".

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

Ця відповідь не має прямого доступу ні до whale.txt, ні до whale2.txt. Він використовує існуючі стандартні бібліотеки стиснення, як явно дозволено правилами.


там може бути "\ r \ n", якого я не міг позбутися в Windows, коли я їх
рахував

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