Napisz około Moby Dicka


297

Oto plik tekstowy ASCII o wielkości 1,2 MB zawierający tekst Moby-Dicka Hermana Melville'a ; lub Wieloryb . Twoim zadaniem jest napisanie programu lub funkcji (lub klasy itp. - patrz poniżej), które otrzymają ten plik po jednym znaku na raz, i na każdym kroku należy odgadnąć następny znak.

To jest . Twój wynik będzie

2*L + E

gdzie Ljest rozmiar twojego przesłania w bajtach, i Eliczba znaków, które odgaduje niepoprawnie. Najniższy wynik wygrywa.

Dalsze szczegóły

Twoje zgłoszenie będzie programem lub funkcją (itp.), Które będą wielokrotnie wywoływane, wywoływane lub wysyłane. (1215235 razy, aby być dokładne). Kiedy to się nazywa po n th czasie będzie mieć n th charakteru whale.txtlub whale2.txti to musi zgadywać jego wyjście dla ( n + 1 ) th charakteru. ESkładnikiem jego wynik będzie łączna liczba znaków, że domyśla się nieprawidłowo.

Większość zgłoszeń będzie musiała przechowywać pewien stan pomiędzy wywołaniami, aby mogli śledzić, ile razy zostały wywołane i jakie były poprzednie dane wejściowe. Możesz to zrobić, pisząc do zewnętrznego pliku, używając staticzmiennych globalnych, podając klasę zamiast funkcji, używając monady stanu lub cokolwiek innego, co działa w twoim języku. Twoje zgłoszenie musi zawierać dowolny kod wymagany do zainicjowania jego stanu przed pierwszym wywołaniem.

Twój program powinien działać deterministycznie, aby zawsze tworzył te same domysły przy tych samych danych wejściowych (a zatem zawsze otrzymuje ten sam wynik).

Twoja odpowiedź musi zawierać nie tylko przesłanie, ale także kod użyty do obliczenia Eczęści wyniku. Nie musi to być napisane w tym samym języku co przesłanie i nie będzie wliczane do liczby bajtów. Zachęcamy do uczynienia go czytelnym.

Jeśli chodzi o interfejs między twoim zgłoszeniem a programem do obliczania wyników, wszystko jest w porządku, o ile twój program zawsze podaje jeden bajt danych wyjściowych przed otrzymaniem kolejnego bajtu danych wejściowych. (Na przykład nie można po prostu przekazać ciągu zawierającego wszystkie dane wejściowe i uzyskać ciąg zawierający wszystkie dane wyjściowe).

Musisz faktycznie uruchomić swój program testowy i obliczyć / zweryfikować swój wynik przed przesłaniem zgłoszenia. Jeśli twoje zgłoszenie przebiega zbyt wolno, abyś mógł zweryfikować swój wynik, nie kwalifikuje się do konkurowania, nawet jeśli wiesz, jaki byłby jego wynik.

LSkładnikiem swój wynik zostanie obliczona zgodnie ze zwykłymi zasadami wyzwań kod golfowych. Jeśli Twoje zgłoszenie będzie zawierać wiele plików, zwróć uwagę na zasady dotyczące oceniania i struktury katalogów w tym przypadku. Wszelkie dane używane przez kod muszą być uwzględnione w Lwyniku.

Możesz importować istniejące biblioteki, ale nie możesz ładować żadnych innych plików zewnętrznych, a twój kod może nie mieć dostępu do whale.txtlubwhale2.txtplik w jakikolwiek inny sposób niż opisano powyżej. Nie możesz ładować żadnych przeszkolonych sieci neuronowych ani innych źródeł danych statystycznych. (Korzystanie z sieci neuronowych jest w porządku, ale musisz dołączyć dane o wadze do swojego zgłoszenia i policzyć je do liczby bajtów.) Jeśli z jakiegoś powodu twój język lub biblioteki zawierają funkcję, która udostępnia część lub całość tekstu Moby Dick , nie możesz korzystać z tej funkcji. Oprócz tego możesz używać dowolnych innych wbudowanych lub bibliotekowych funkcji, w tym związanych z przetwarzaniem tekstu, prognozowaniem lub kompresją, o ile są one częścią twojego języka lub jego standardowych bibliotek. W przypadku bardziej egzotycznych, specjalistycznych procedur, które zawierają źródła danych statystycznych, należy je zaimplementować samodzielnie i uwzględnić w liczbie bajtów.

Prawdopodobnie niektóre zgłoszenia będą zawierać komponenty, które same są generowane przez kod. W takim przypadku prosimy o dołączenie do odpowiedzi kodu użytego do ich wytworzenia i wyjaśnienie, jak to działa . (Tak długo, jak ten kod nie jest potrzebny do uruchomienia przesyłania, nie będzie uwzględniony w liczbie bajtów).

Ze względów historycznych istnieją dwie wersje pliku i możesz użyć jednej z nich w odpowiedzi. W whale2.txt(połączony powyżej) tekst nie jest zawijany, więc znaki nowej linii pojawiają się tylko na końcu akapitów. W oryginale whale.txttekst jest zawinięty do szerokości 74 znaków, więc musisz przewidzieć koniec każdej linii, a także przewidzieć tekst. To sprawia, że ​​wyzwanie jest bardziej skomplikowane, dlatego whale2.txtjest zalecane w przypadku nowych odpowiedzi. Oba pliki mają ten sam rozmiar, 1215236 bajtów.


Podsumowując, wszystkie odpowiedzi powinny zawierać następujące rzeczy:

  • Twoje samo zgłoszenie. (Kod oraz wszelkie używane przez niego pliki danych - mogą być linkami, jeśli są duże).
  • Wyjaśnienie, jak działa Twój kod. Proszę wyjaśnić metodę we / wy, a także sposób przewidywania następnego znaku. Wyjaśnienie twojego algorytmu jest ważne, a dobre wyjaśnienia przyniosą ode mnie nagrody.
  • Kod użyty do oceny wyniku. (Jeśli jest to identyczna z poprzednią odpowiedzią, możesz po prostu link do niej.)
  • Każdy kod użyty do wygenerowania zgłoszenia wraz z objaśnieniem tego kodu. Obejmuje to kod użyty do optymalizacji parametrów, generowania plików danych itp. (Nie wlicza się to do liczby bajtów, ale powinno zostać uwzględnione w odpowiedzi).

Tabela liderów

Nagrody

Od czasu do czasu oferuję nagrody, aby zachęcić do różnych podejść.

Pierwszy, 50 punktów, został przyznany A. Rexowi za najlepszą wówczas odpowiedź.

Druga, 100 punktów, została również przyznana A. Rexowi za tę samą odpowiedź, ponieważ dodali bardzo dobre wyjaśnienie do swojej istniejącej odpowiedzi.

Następna nagroda, 200 punktów , zostanie przyznana jednemu z nich

  • Konkurencyjna odpowiedź wykorzystująca nową technikę. (Będzie to oparte na moim subiektywnym osądzie, ponieważ to mój przedstawiciel wchodzi na nagrodę, ale możesz mi zaufać, że jestem uczciwy. Pamiętaj, że twoja odpowiedź musi zawierać wystarczające wyjaśnienie, aby zrozumieć, jak to działa!) Takiej odpowiedzi potrzebowałem nie ma najwyższego wyniku, musi po prostu dobrze sobie poradzić w porównaniu z istniejącymi odpowiedziami. Szczególnie zależy mi na rozwiązaniach opartych na cyklicznych sieciach neuronowych, ale nagrodę przyznam za wszystko, co wydaje się wystarczająco inne niż modele Markowa, które dominują w obecnych najlepszych wynikach.

Lub:

  • Każdy inny, kto bije najlepszy wynik A. Rexa (obecnie 444444), przy użyciu dowolnej metody.

Po odebraniu nagrody za 200 punktów najprawdopodobniej zaoferuję 400 punktów, odpowiednio aktualizując wymagania.


Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Dennis

9
xkcd.com/1960 wydaje się być odniesieniem do tego wyzwania!
A. Rex,

Myślałem o skompresowaniu tego ... ale to trochę za długo, że mój komputer rozbił się ramionami
Naruyoko,

Odpowiedzi:



97

Node.js, 2 * 224 + 524279 = 524727

Informacje na temat aktualizacji wyników można znaleźć w dzienniku zmian na końcu tego postu.

Funkcja pobierająca i zwracająca bajt.

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

Składa się z prostego modelu PPM, który patrzy na ostatnie 8 znaków, aby przewidzieć następny.

Ufamy wzorowi długości L, gdy napotkaliśmy go co najmniej T [L] razy, gdzie T jest tablicą dowolnych progów: [1,1,2,1,2,3,5,2] . Ponadto zawsze ufamy wzorowi, którego pierwsza postać pasuje [A-Z '"(].

Wybieramy najdłuższy zaufany wzór i zwracamy prognozę z najwyższym wynikiem związanym z tym wzorem w momencie połączenia.

Notatki

  • To oczywiście nie jest zoptymalizowane pod kątem prędkości, ale działa na około 15 sekundach na moim laptopie.

  • Gdybyśmy mogli powtórzyć proces kilka razy z rzędu bez resetowania modelu, liczba błędów zbiegnie się do ~ 268000 po 5 iteracjach.

  • Aktualny wskaźnik skuteczności funkcji prognozowania wynosi ~ 56,8%. Jak zauważył @immibis w komentarzach, jeśli błędne i prawidłowe domysły zostaną zmieszane razem, wynik nie będzie nawet ledwo czytelny.

    Na przykład ten fragment kodu na końcu książki:

    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.
    

    staje się:

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

    Zastępując błędne domysły znakami podkreślenia, mamy lepsze pojęcie o tym, co funkcja zadziałała:

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

    Uwaga : powyższy przykład został utworzony przy użyciu poprzedniej wersji kodu, działającej na pierwszej wersji pliku wejściowego.

Kod testowy

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

Zmień dziennik

  • 524727 - zapisano 19644 punktów, przechodząc na whale2.txt (aktualizacja wyzwania)
  • 544371 - zaoszczędzono 327 punktów, zmuszając wzorce zaczynające się od dużej litery, cytatu, podwójnego cytatu lub nawiasu otwierającego, aby zawsze być zaufanym
  • 544698 - zapisano 2119 punktów, zmuszając wzorce rozpoczynające się od miejsca, któremu zawsze można zaufać
  • 546817 - zaoszczędzono 47 punktów, dostosowując progi i grając w funkcję przewidywania
  • 546864 - zapisano 1496 punktów poprzez zwiększenie maksymalnej długości wzoru do 8 znaków
  • 548360 - zapisano 6239 punktów, wprowadzając pojęcie zaufanych wzorów, z progami zależnymi od ich długości
  • 554599 - zaoszczędzono 1030 punktów, poprawiając przewidywanie przesunięcia linii
  • 555629 - zaoszczędzono 22 punkty, grając w golfa w funkcji przewidywania
  • 555651 - zapisano 40 punktów, grając w golfa w funkcji przewidywania
  • 555691 - wynik początkowy

44
Dla ciekawskich nie, to nie produkuje niczego takiego jak Moby Dick. To dużo 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. Czasami potrafi uzyskać kilka pełnych słów. Jak whales.
immibis

23
@immibis Tytuł wyzwania został wybrany mądrze. To w przybliżeniu Moby Dick . :-)
Arnauld

3
@Nathaniel Wprowadzono wiele aktualizacji, więc byłyby one ledwo czytelne i mało pouczające. Zamiast tego dodałem dziennik zmian z krótkimi objaśnieniami dotyczącymi ulepszeń.
Arnauld,

45
Myślę, że twój program faktycznie wykonuje idealne tłumaczenie na gaelicki.
Beska

1
@ Draco18s Trudno powiedzieć, czy ten przecinek był dobry, czy zły. Gdyby było to błędne przypuszczenie, funkcja przewidywania mogła słusznie spróbować umieścić literę po czymkolwiek innym, który faktycznie był tam zamiast przecinka, po jego otrzymaniu.
Arnauld,

91

Perl, 2 · 70525 + 326508 = 467558

Urządzenie prognozujące

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

Aby uruchomić ten program, potrzebujesz tego pliku tutaj , który musi mieć nazwę B. (Możesz zmienić tę nazwę pliku w drugim wystąpieniu Bpowyższego znaku .) Zobacz poniżej, jak wygenerować ten plik.

Program wykorzystuje kombinację modeli Markowa zasadniczo jak w tej odpowiedzi użytkownika 2699 , ale z kilkoma niewielkimi modyfikacjami. To tworzy rozkład dla następnej postaci. Korzystamy z teorii informacji, aby zdecydować, czy zaakceptować błąd, czy wydać fragmenty pamięci na Bkodowanie wskazówek (a jeśli tak, to w jaki sposób). Używamy kodowania arytmetycznego do optymalnego przechowywania ułamkowych bitów z modelu.

Program ma długość 582 bajtów (w tym niepotrzebną końcową Bnową linię ), a plik binarny ma długość 69942 bajtów, więc zgodnie z regułami oceniania wielu plików oceniamy Ljako 582 + 69942 + 1 = 70525.

Program prawie na pewno wymaga architektury 64-bitowej (little-endian?). Uruchomienie m5.largeinstancji na Amazon EC2 zajmuje około 2,5 minuty .

Kod testowy

# 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

Wiązka testowa zakłada, że ​​przesłanie znajduje się w pliku submission.pl, ale można to łatwo zmienić w drugim wierszu.

Porównanie tekstu

"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

Ta próbka (wybrana w innej odpowiedzi ) pojawia się dość późno w tekście, więc model jest dość rozwinięty do tego momentu. Pamiętaj, że model jest powiększony o 70 kilobajtów „podpowiedzi”, które bezpośrednio pomagają odgadnąć postacie; nie jest napędzany po prostu krótkim fragmentem kodu powyżej.

Generowanie wskazówek

Poniższy program akceptuje dokładny kod przesłania powyżej (na standardowym wejściu) i generuje dokładny Bplik powyżej (na standardowym wyjściu):

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

Uruchomienie zajmuje około tyle samo czasu co przesłanie, ponieważ wykonuje podobne obliczenia.

Wyjaśnienie

W tej sekcji postaramy się opisać wystarczająco szczegółowo to rozwiązanie, abyś mógł sam „wypróbować go w domu”. Główną techniką, która odróżnia tę odpowiedź od innych, jest kilka sekcji w dół jako mechanizm „przewijania”, ale zanim do tego dojdziemy, musimy skonfigurować podstawy.

Model

Podstawowym składnikiem rozwiązania jest model językowy. Dla naszych celów model jest czymś, co wymaga pewnej ilości tekstu w języku angielskim i zwraca rozkład prawdopodobieństwa dla następnego znaku. Gdy użyjemy modelu, tekst w języku angielskim będzie jakimś (poprawnym) prefiksem Moby Dicka. Należy pamiętać, że pożądanym wyjściem jest rozkład , a nie tylko jedno odgadnięcie najbardziej prawdopodobnej postaci.

W naszym przypadku zasadniczo używamy modelu w tej odpowiedzi użytkownika 2699 . Nie wykorzystaliśmy modelu z odpowiedzi o najwyższym wyniku (innej niż nasza) Andersa Kaseorga właśnie dlatego, że nie byliśmy w stanie wyodrębnić dystrybucji, a nie pojedynczego najlepszego przypuszczenia. Teoretycznie ta odpowiedź oblicza ważoną średnią geometryczną, ale otrzymaliśmy nieco słabe wyniki, gdy interpretowaliśmy to zbyt dosłownie. „Ukradliśmy” model z innej odpowiedzi, ponieważ nasz „sekretny sos” nie jest modelem, ale raczej ogólnym podejściem. Jeśli ktoś ma „lepszy” model, powinien być w stanie uzyskać lepsze wyniki przy użyciu pozostałych naszych technik.

Jako uwaga, większość metod kompresji, takich jak Lempel-Ziv, może być postrzegana jako „model językowy” w ten sposób, choć może trzeba trochę zmrużyć oczy. (Jest to szczególnie trudne w przypadku czegoś, co powoduje transformację Burrowsa-Wheelera!) Ponadto zauważ, że model użytkownika 2699 jest modyfikacją modelu Markowa; zasadniczo nic innego nie jest konkurencyjne w stosunku do tego wyzwania, a może nawet do modelowania tekstu w ogóle.

Ogólna architektura

Dla zrozumienia dobrze jest podzielić ogólną architekturę na kilka części. Z perspektywy najwyższego poziomu musi być trochę kodu zarządzania stanem. Nie jest to szczególnie interesujące, ale dla kompletności chcemy podkreślić, że w każdym momencie program jest proszony o kolejne przypuszczenia, ma dostępny poprawny prefiks Moby Dick. W żaden sposób nie wykorzystujemy naszych niepoprawnych domysłów. Ze względu na wydajność, model językowy może prawdopodobnie ponownie użyć swojego stanu z pierwszych N znaków do obliczenia swojego stanu dla pierwszych (N + 1) znaków, ale w zasadzie może za każdym razem przywoływać obliczenia od zera.

Odłóżmy ten podstawowy „sterownik” programu na bok i zajrzyjmy do części odgadującej następny znak. Pomaga koncepcyjnie oddzielić trzy części: model języka (omówiony powyżej), plik „podpowiedzi” i „tłumacza”. Na każdym kroku tłumacz zapyta model językowy o rozkład dla następnego znaku i może odczytać pewne informacje z pliku wskazówek. Następnie połączy te części w zgadywanie. Dokładnie, jakie informacje znajdują się w pliku wskazówek, a także jak są wykorzystywane zostaną wyjaśnione później, ale na razie pomaga zachować te części osobno. Zauważ, że pod względem implementacji plik wskazówek jest dosłownie osobnym (binarnym) plikiem, ale mógł być łańcuchem lub czymś przechowywanym w programie. W przybliżeniu

Jeśli ktoś używa standardowej metody kompresji, takiej jak bzip2, jak w tej odpowiedzi , plik „podpowiedzi” odpowiada skompresowanemu plikowi. „Tłumacz” odpowiada dekompresorowi, podczas gdy „model językowy” jest nieco niejawny (jak wspomniano powyżej).

Dlaczego warto korzystać z pliku podpowiedzi?

Wybierzmy prosty przykład do dalszej analizy. Załóżmy, że tekst to Nznaki długie i dobrze przybliżone przez model, w którym każdy znak jest (niezależnie) literą Ez prawdopodobieństwem nieco mniejszym niż połowa, Tpodobnie z prawdopodobieństwem nieco mniejszym niż połowa, i Az prawdopodobieństwem 1/1000 = 0,1%. Załóżmy, że żadne inne postacie nie są możliwe; w każdym razie Ajest bardzo podobny do przypadku wcześniej niewidzianej postaci nieoczekiwanej.

Jeśli działaliśmy w trybie L 0 (jak większość, ale nie wszystkie, inne odpowiedzi na to pytanie), nie ma lepszej strategii dla tłumacza niż wybranie jednego z Ei T. Średnio uzyska około połowy poprawnych znaków. Więc E ≈ N / 2 i wynik ≈ N / 2 również. Jeśli jednak zastosujemy strategię kompresji, możemy skompresować ją do nieco więcej niż jednego bitu na znak. Ponieważ L jest liczone w bajtach, otrzymujemy L ≈ N / 8 i tym samym osiągamy ≈ N / 4, dwa razy więcej niż poprzednia strategia.

Osiągnięcie tej szybkości nieco więcej niż jednego bitu na znak dla tego modelu jest nieco nietrywialne, ale jedną z metod jest kodowanie arytmetyczne.

Kodowanie arytmetyczne

Jak powszechnie wiadomo, kodowanie jest sposobem reprezentowania niektórych danych przy użyciu bitów / bajtów. Na przykład ASCII to 7-bitowe kodowanie znaków w tekście angielskim i powiązanych znakach, a także kodowanie oryginalnego rozważanego pliku Moby Dick. Jeśli niektóre litery są bardziej powszechne niż inne, wówczas kodowanie o stałej szerokości, takie jak ASCII, nie jest optymalne. W takiej sytuacji wiele osób sięga po kodowanie Huffmana . Jest to optymalne, jeśli chcesz mieć stały (bez prefiksów) kod z liczbą całkowitą bitów na znak.

Jednak kodowanie arytmetyczne jest jeszcze lepsze. Z grubsza mówiąc, jest w stanie używać „ułamkowych” bitów do kodowania informacji. Istnieje wiele przewodników po kodowaniu arytmetycznym dostępnych online. Pominiemy tutaj szczegóły (szczególnie praktyczne wdrożenie, które może być nieco trudne z punktu widzenia programowania) ze względu na inne zasoby dostępne online, ale jeśli ktoś narzeka, może ten rozdział można rozwinąć bardziej.

Jeśli tekst jest generowany przez znany model językowy, wówczas kodowanie arytmetyczne zapewnia zasadniczo optymalne kodowanie tekstu z tego modelu. W pewnym sensie „rozwiązuje” to problem kompresji tego modelu. (Tak więc w praktyce głównym problemem jest to, że model nie jest znany, a niektóre modele są lepsze niż inne w modelowaniu tekstu ludzkiego.) Jeśli nie dopuszczono się błędów w tym konkursie, to w języku poprzedniej sekcji , jednym ze sposobów na rozwiązanie tego problemu byłoby użycie kodera arytmetycznego do wygenerowania pliku „wskazówek” z modelu językowego, a następnie użycie dekodera arytmetycznego jako „interpretera”.

W tym zasadniczo optymalnym kodowaniu ostatecznie wydajemy -log_2 (p) bitów na znak o prawdopodobieństwie p, a ogólną przepływnością kodowania jest entropia Shannona . Oznacza to, że znak o prawdopodobieństwie bliskim 1/2 zajmuje około jednego bitu do zakodowania, a znak o prawdopodobieństwie 1/1000 zajmuje około 10 bitów (ponieważ 2 ^ 10 to w przybliżeniu 1000).

Ale wskaźnik punktacji dla tego wyzwania został dobrze wybrany, aby uniknąć kompresji jako optymalnej strategii. Będziemy musieli wymyślić jakiś sposób na popełnienie błędów jako kompromis w uzyskaniu krótszego pliku wskazówek. Na przykład jedna strategia, którą można wypróbować, jest prostą strategią rozgałęziania: na ogół staramy się stosować kodowanie arytmetyczne, kiedy tylko możemy, ale jeśli rozkład prawdopodobieństwa z modelu jest „zły”, to po prostu odgadujemy najbardziej prawdopodobną postać i nie „ spróbuj go zakodować.

Po co popełniać błędy?

Przeanalizujmy wcześniejszy przykład, aby uzasadnić, dlaczego możemy chcieć popełniać błędy „umyślnie”. Jeśli użyjemy kodowania arytmetycznego do zakodowania poprawnego znaku, wydamy około jednego bitu w przypadku Elub T, ale około dziesięciu bitów w przypadku A.

Ogólnie rzecz biorąc, jest to całkiem dobre kodowanie, wydające nieco ponad nieco na postać, mimo że istnieją trzy możliwości; w zasadzie Ajest to raczej mało prawdopodobne i nie spędzamy zbyt często odpowiadających mu dziesięciu bitów. Czy jednak nie byłoby miło, gdybyśmy mogli zamiast tego popełnić błąd w przypadku A? W końcu metryka problemu uważa, że ​​1 bajt = 8 bitów długości jest równoważny 2 błędom; dlatego wydaje się, że należy preferować błąd zamiast wydawać więcej niż 8/2 = 4 bity na znak. Wydanie więcej niż bajtu, aby zapisać jeden błąd, zdecydowanie brzmi nieoptymalnie!

Mechanizm „przewijania do tyłu”

W tej sekcji opisano główny sprytny aspekt tego rozwiązania, który jest sposobem obsługi niepoprawnych domysłów bez żadnych kosztów.

W tym prostym przykładzie, który analizowaliśmy, mechanizm przewijania jest szczególnie prosty. Tłumacz interpretuje jeden bit z pliku wskazówek. Jeśli jest to 0, zgaduje E. Jeśli jest to 1, zgaduje T. Przy następnym wywołaniu zobaczy, jaki jest prawidłowy znak. Jeśli plik podpowiedzi jest dobrze skonfigurowany, możemy zapewnić, że w przypadku Elub Tinterpreter zgadnie poprawnie. Ale co z tym A? Pomysł z mechanizmem zwijania jest po prostu nie kodować Aw ogóle . Mówiąc ściślej, jeśli interpreter dowie się później, że poprawny znak był A, metaforycznie „ przewija taśmę”: zwraca fragment, który wcześniej przeczytał. Bit, który odczytuje, ma zamiar kodować ElubT, ale nie teraz; zostanie wykorzystany później. W tym prostym przykładzie, w zasadzie oznacza to, że utrzymuje zgadywania ten sam znak ( Elub T), aż robi się to dobrze; potem odczytuje kolejny kawałek i kontynuuje.

Kodowanie tego pliku wskazówek jest bardzo proste: zamień wszystkie Es na 0 bitów is Tna 1 bit, jednocześnie Acałkowicie ignorując s. Według analizy pod koniec poprzedniej sekcji, ten schemat popełnił pewne błędy, ale zmniejsza ogólny wynik, nie kodując żadnego z nich A. Jako mniejszy efekt, faktycznie oszczędza również na długości pliku wskazówek, ponieważ ostatecznie używamy dokładnie jednego bitu na każdy Ei Tzamiast nieco więcej niż nieco.

Trochę twierdzenia

Jak decydujemy, kiedy popełnić błąd? Załóżmy, że nasz model podaje rozkład prawdopodobieństwa P dla następnego znaku. Rozdzielimy możliwe znaki na dwie klasy: zakodowane i niekodowane . Jeśli prawidłowy znak nie zostanie zakodowany, wówczas skorzystamy z mechanizmu „przewijania”, aby zaakceptować błąd bez żadnych kosztów. Jeśli prawidłowy znak jest zakodowany, użyjemy innej dystrybucji Q do zakodowania go za pomocą kodowania arytmetycznego.

Ale jaki rozkład Q wybrać? Nietrudno dostrzec, że wszystkie zakodowane znaki powinny mieć większe prawdopodobieństwo (w P) niż znaki niekodowane. Także rozkład Q powinien zawierać tylko zakodowane znaki; w końcu nie kodujemy pozostałych, więc nie powinniśmy „wydawać” na nie entropii. Trochę trudniej jest dostrzec, że rozkład prawdopodobieństwa Q powinien być proporcjonalny do P na zakodowanych znakach. Zebranie tych obserwacji razem oznacza, że ​​powinniśmy kodować najbardziej prawdopodobne znaki, ale być może nie mniej prawdopodobne, i że Q jest po prostu przeskalowane P na znakach kodowanych.

Okazuje się ponadto, że istnieje fajne twierdzenie dotyczące tego, który „punkt odcięcia” należy wybrać dla znaków kodujących: należy zakodować znak, o ile jest on co najmniej 1 / 5.393 tak prawdopodobny, jak inne zakodowane znaki łącznie. To „wyjaśnia” pojawienie się pozornie losowej stałej 5.393bliżej końca powyższego programu. Liczba 1 / 5,393 × 0,18542 jest rozwiązaniem równania -p log (16) - p log p + (1 + p) log (1 + p) = 0 .

Być może rozsądnym pomysłem jest zapisanie tej procedury w kodzie. Ten fragment jest w 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;
}

Kładąc wszystko razem

Poprzednia sekcja jest niestety trochę techniczna, ale jeśli połączymy wszystkie pozostałe elementy razem, struktura będzie następująca. Ilekroć program jest proszony o przewidzenie następnego znaku po danym poprawnym znaku:

  1. Dodaj poprawny znak do znanego poprawnego prefiksu Moby Dick.
  2. Zaktualizuj model tekstu (Markowa).
  3. Tajny sos : Jeśli poprzednie przypuszczenie było błędne, przewijanie stan dekodera arytmetycznego do stanu sprzed poprzedniego odgadnięcia!
  4. Poproś model Markowa, aby przewidział rozkład prawdopodobieństwa P dla następnej postaci.
  5. Przekształć P w Q, używając podprogramu z poprzedniej sekcji.
  6. Poproś dekoder arytmetyczny, aby zdekodował znak z pozostałej części pliku wskazówek, zgodnie z rozkładem Q.
  7. Zgadnij wynikową postać.

Kodowanie pliku wskazówek działa podobnie. W takim przypadku program wie, jaki jest poprawny następny znak. Jeśli jest to znak, który należy zakodować, to oczywiście należy użyć kodera arytmetycznego na nim; ale jeśli jest to niekodowany znak, po prostu nie aktualizuje stanu enkodera arytmetycznego.

Jeśli rozumiesz tło teoretyczne informacji, takie jak rozkłady prawdopodobieństwa, entropia, kompresja i kodowanie arytmetyczne, ale próbowałeś i nie zrozumiał tego postu (z wyjątkiem tego, dlaczego twierdzenie jest prawdziwe), daj nam znać, a my możemy spróbować wyjaśnić sytuację. Dziękuje za przeczytanie!


8
Wow, imponująca odpowiedź. Zakładam, że do wygenerowania Bpliku potrzebny jest dodatkowy kod ? Jeśli tak, czy możesz to uwzględnić w swojej odpowiedzi?
Nathaniel

8
Doskonały! Pierwsza (i jak dotąd jedyna) odpowiedź na przełamanie bariery wynikowej 500 tys.
ShreevatsaR

5
„tersed the shite whale” omg Płaczę
Phill

5
Ponieważ nie otrzymano żadnych nowych odpowiedzi w okresie nagród, przyznam je twojej odpowiedzi, zarówno jako najlepszy wynik, jak i najbardziej wyrafinowane podejście. Jeśli kiedykolwiek będziesz miał czas, naprawdę doceniłbym bardziej dogłębne wyjaśnienie, w jaki sposób ta odpowiedź działa, tj. Czym dokładnie jest algorytm?
Nathaniel

2
@Naniel: Dodałem wyjaśnienie do tego postu. Daj mi znać, jeśli uważasz, że jest wystarczająco szczegółowy, aby samodzielnie odtworzyć rozwiązanie.
A. Rex

77

Python 3, 2 · 267 + 510193 = 510727

Urządzenie prognozujące

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]

Wykorzystuje ważoną kombinację bayesowską modeli 0,…, 16 modeli Markowa, z wagami [1, 6, 12, 30, 65, 99, 87, 117, 118, 89, 95, 118, 96, 184, 126, 219, 126].

Wynik nie jest bardzo wrażliwy na dobór tych wag, ale zoptymalizowałem je, ponieważ mogłem, stosując ten sam algorytm wspinaczki późnej akceptacji , który zastosowałem w odpowiedzi na „Ustaw większość senacką” , gdzie każda mutacja kandydująca jest zaledwie przyrost o ± 1 do pojedynczej masy.

Kod testowy

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
Właściwe narzędzie do pracy. Świetny wynik. Niezłe.
przeciwnie

1
Możliwe wyjaśnienie: b"\0\3\6\r\34'&-20'\22!P\n[\26"to ascii reprezentacja wag, w której małe wartości niedrukowalne są unikane w postaci ósemkowej.
Cœur

Zaktualizowałem pytanie o wersję pliku, w której tekst nie jest zawijany - możesz spróbować ponownie uruchomić na nim swój kod (może być nieco lepiej)
Nathaniel

3
Dziękuję za to wyjaśnienie - gdybyś mógł edytować streszczenie pytania, byłoby świetnie. (Doświadczenie z moim poprzednim wyzwaniem Paint Starry Night jest takie, że te procedury optymalizacji są najciekawszą częścią odpowiedzi, więc znacznie lepiej jest, jeśli odpowiedzi zawierają kod użyty do tego celu i wyjaśnienie tego. Zawarłem regułę w obu wyzwania, które mówią, że powinni.)
Nathaniel,

1
@Christoph Moja kombinacja modeli jest właściwie ważoną średnią geometryczną. Ale uśrednianie PAQ w dziedzinie logistyki jest nieco inne - muszę sprawdzić, czy to lepiej.
Anders Kaseorg

55

Python 3 , 2 * 279 + 592920 = 593478 2 * 250 + 592467 = 592967 2 * 271 + 592084 = 592626 2 * 278 + 592059 = 592615 2 * 285 + 586660 = 587230 2 * 320 + 585161 = 585801 2 * 339 + 585050 = 585728

d=m={}
s=1
w,v='',0
def f(c):
 global w,m,v,s,d
 if w not in m:m[w]={}
 u=m[w];u[c]=c in u and 1+u[c]or 1;v+=1;q=n=' ';w=w*s+c;s=c!=n
 if w in m:_,n=max((m[w][k],k)for k in m[w])
 elif s-1:n=d in'nedtfo'and't'or'a'
 elif'-'==c:n=c
 elif"'"==c:n='s'
 elif'/'<c<':':n='.'
 if v>4*(n!=q)+66:n='\n'
 if s:d=c
 if c<q:w=w[:-1]+q;v=s=0
 return n

Wypróbuj online!

Funkcja wykorzystująca zmienne globalne. Uczy się, jak to się dzieje, budując model na poziomie słowa: biorąc pod uwagę to, co do tej pory widać w tym słowie , jaka jest najczęstsza następna postać? W miarę, jak pojawia się w nim więcej informacji, dość dobrze uczy się popularnych słów z tekstu, a także uczy najczęstszych znaków rozpoczynających następne słowo.

Na przykład:

  • Jeśli do tej pory widziano „Captai”, przewiduje „n”
  • Jeśli jest to „Kapitan”, przewiduje przestrzeń
  • Jeśli jest to początek słowa, a ostatnim słowem było „Kapitan”, przewiduje „A”
  • Jeśli dotychczasowe słowo to „A”, przewiduje „h” (a następnie „a” i „b”; podobnie dla „C”).

Na początku nie radzi sobie zbyt dobrze, ale pod koniec pojawiają się duże części prawdziwych słów. Opcją zastępczą jest spacja, a po pojedynczym spacji jest to „a”, chyba że poprzednia litera była jedną z „nedtfo”, cyfry, łącznika lub apostrofu. Agresywnie przewiduje także łamanie wiersza po 71 znakach lub spację po 66. Oba zostały właśnie dostrojone do danych („t” jest znacznie bardziej powszechne po spacji, ale częściej jest już przewidywane, więc „ „lepiej zgadnąć poza tymi sześcioma specjalnymi przypadkami”.

Uczenie się, jakie pary słów idą w parze i uprzedzanie mapowania, okazało się nie opłacalne.


Kończy się na nim taki tekst:

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!

co odpowiada tej części danych wejściowych:

wokół niego.

„Widziałem go niemal w tej samej chwili, co kapitan Ahab, i krzyknąłem” - powiedział Tashtego.

„Nie w tej samej chwili; nie w tym samym - nie, dublon jest mój, Los zarezerwował dla mnie dublon. Ja tylko; nikt z was nie mógł najpierw wychować Białego Wieloryba. Tam ona dmucha! - tam ona dmucha! - - tam ona wieje!

Widać, gdzie w szczególności właściwe rzeczowniki wychodzą całkiem nieźle, ale końcówki słów są również w większości właściwe. Kiedy zobaczysz „dou”, oczekuje „wątpliwości”, ale gdy pojawi się „l”, staje się „dublonem”.

Jeśli uruchomisz go po raz drugi z tym samym modelem, który właśnie zbudował, natychmiast uzyska kolejne 92k poprawności (51,7% -> 59,3%), ale zawsze jest nieco poniżej 60% od drugiej iteracji.


Kod pomiarowy znajduje się w łączu TIO lub oto nieco lepsza wersja:

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 ma zgadnięte wyjście na końcu.


3
To doskonałe podejście!
Skyler

2
za dużo <s> </s>;)
FantaC 14.01.18

1
+1, ponieważ takie podejście przypomina mi algorytm kompresji LZW.
Marcos,

25

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

Dzięki @Quentin za oszczędność 217 bajtów!

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

Bardzo proste rozwiązanie, które po podaniu znaku wypisuje znak, który najczęściej pojawia się po znaku wejściowym.

Sprawdź wynik za pomocą:

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

Edycja: Używanie whale2.txtdaje lepszy wynik.


5
Możesz przetłumaczyć tę tablicę na literał łańcuchowy i wstawić bezpośrednio w miejsce, Laby zapisać kilka znaków :)
Quentin

@Quentin Thanks! Teraz zastanawiam się, dlaczego w ogóle o tym nie pomyślałem ...
Steadybox 10.01.18

20

Python, 2 * 516 + 521122 = 522154

Algorytm:

Jeszcze inne przesłanie pytona, ten algorytm oblicza najbardziej prawdopodobną następną literę, patrząc na sekwencje o długości 1, ..., l. Wykorzystywana jest suma prawdopodobieństw i istnieje kilka sztuczek, aby uzyskać lepsze wyniki.

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

Wyniki:

Przeważnie bełkot, choć można go zauważyć, od czasu do czasu pojawia się w zwrotach, takich jak „Ojciec 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

Kod testowy:

Całkiem proste, wyświetla kilka przykładów tekstu w różnych punktach. Używa pliku whale2.txt, ponieważ pozwala to uniknąć dodatkowej logiki obliczania nowych linii.

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
Witamy na stronie! To fantastyczne pierwsze zgłoszenie. :)
DJMcMayhem

@DJMcMayhem, dzięki za powitanie. Od dłuższego czasu lubię oglądać. To pierwszy konkurs, który przykuł moją uwagę na zgłoszenie.
user2699

19

C (gcc) , 679787 652892

84 76 bajtów, 679619 652740 błędne domysły

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

Wypróbuj online!

Aktualizacja: ~ 27000 punktów zniżki przy zaktualizowanym pliku, 16 punktów (8 bajtów) z funkcją lepszej gry w golfa.

Wyjaśnienie

Działa to w ten sposób, że gdy kod przechodzi przez tekst, zapamiętuje ostatni znak, który zakończył dowolną 4-znakową sekwencję, i zwraca tę wartość. Nieco podobny do powyższego podejścia Arnaulda, ale opiera się na nieodłącznym prawdopodobieństwie dwóch danych sekwencji 4-znakowych kończących się w ten sam sposób.

Gra w golfa:

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

... Łącze TIO jest bezużyteczne. Czy funkcja zwraca wartość ostatniego przypisania?
user202729,

Pozwól mi edytować odpowiedź z wyjaśnieniem, a następnie :)

1
@Rogem Dodałem wersję do gry w golfa (co zrobiłem, ponieważ ja też nie mogłem jej śledzić) - mam nadzieję, że to nie przeszkadza, ale w razie potrzeby wycofaj ją.
Adam Davis,

@AdamDavis w większości implementacji C, wszystkie zmienne globalne zaczynają się od zera. To nieokreślone zachowanie, więc jest używane tylko w golfa.
NieDzejkob,

1
@NieDzejkob Ach, masz rację, dzięki! „ANSI-C wymaga, aby wszystkie niezainicjowane zmienne statyczne / globalne były inicjowane na 0.”
Adam Davis,

16

sh + bzip2, 2 * 364106 = 728212

2 * 381249 + 0 = 762498

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

a następnie skompresowany przez bzip2 plik whale2.txt z brakującym pierwszym bajtem

Ignoruje swój wkład; wypisuje poprawną odpowiedź. Zapewnia to linię bazową na jednym końcu; daniero zapewnia linię bazową na drugim końcu.

Skrypt konstruktora:

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

Wiązka testowa we / wy (tcc; odetnij pierwszą linię dla gcc). Ta uprząż testowa może być używana przez każdego na odpowiedniej platformie, która przesyła pełny program, który oczekuje odczytu / zapisu We / Wy. Używa bajtów we / wy, aby uniknąć oszustwa. Program potomny musi opróżniać wyjście po każdym bajcie, aby uniknąć blokowania.

#!/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
Myślę, że pyta on: jak to nie narusza 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.klauzuli?

8
@Rogem Skompresowane dane są umieszczane po tym, co pokazano tutaj, i dostęp do samego kodu.
user202729,

4
Pytanie brzmi: „Twoje zgłoszenie będzie programem lub funkcją (itp.), Które będą wywoływane lub wywoływane wiele razy. Kiedy zostanie wywołane na nthczas, otrzyma n-ty znak whale.txtlub whale2.txti musi wypisać przypuszczenie (n+1)thpostać." - Jak spełniony jest ten wymóg? Kod wyświetla cały tekst za whale.txtkażdym razem, gdy jest wykonywany.
axiac

1
@axiac „wszystko jest w porządku, o ile twój program zawsze podaje jeden bajt danych wyjściowych przed otrzymaniem następnego bajtu danych wejściowych.”
user202729

5
@axiac, biorąc pod uwagę uprząż testową, cieszę się, że wysyłam programowi jeden bajt ze STDIN jako „wywoływanie go lub wywoływanie”. Import polega na tym, że program zwraca jeden bajt wyjścia po każdym bajcie danych wejściowych, co tak naprawdę robi, po uruchomieniu przez wiązkę testową. Jak mówi pytanie: „wszystko jest w porządku, o ile twój program zawsze podaje jeden bajt danych wyjściowych przed otrzymaniem kolejnego bajtu danych wejściowych”.
Nathaniel

13

Python 3 , 879766

F=[[0]*123for _ in range(123)]
P=32
def f(C):global P;C=ord(C);F[P][C]+=1;P=C;return chr(max(enumerate(F[C]),key=lambda x:x[1])[0])

Wypróbuj online!


... The /// Odpowiedź, która wypisuje spację, otrzymuje 10 upvotes, podczas gdy mój kod może dostać tylko 3 ...

Wyjaśnienie:

Dla każdej postaci program:

  • zwiększać frequency[prev][char]
  • Znajdź postać, która pojawia się najczęściej frequency[char]
  • i wyślij to.

  • Skomentowano niepoznany kod w linku TIO.
  • Kod ma 131 bajtów.
  • Kod uruchamiany na moich komputerach zgłasza:
879504 / 1215235
Time: 62.01348257784468

które mają łączny wynik

2*131 + 879504 = 879766

Ponieważ nie ma możliwości przesłania dużego pliku do TIO (poza pytaniem Dennisa), przykładowe uruchomienie w łączu TIO uruchamia program tylko dla niewielkiej części tekstu.

W porównaniu ze starszą odpowiedzią ta zawiera 362 więcej niepoprawnych znaków, ale kod jest krótszy o 255 bajtów. Mnożnik sprawia, że ​​moje zgłoszenie ma niższy wynik.


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

Podejście to wykorzystuje tabelę przeglądową, aby poznać najczęstszy znak występujący po danym ciągu. Klawisze tabeli przeglądowej mają maksymalnie 4 znaki, więc funkcja najpierw aktualizuje tabelę przeglądową bieżącym znakiem, a następnie sprawdza, która postać jest najbardziej prawdopodobna po 4 poprzednich, w tym bieżącej . Jeśli te 4 znaki nie zostaną znalezione w tabeli przeglądowej, drukuje spację.

Ta wersja używa whale2.txt pliku, ponieważ znacznie poprawia liczbę udanych zgadnięć.

Oto kod użyty do przetestowania klasy:

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

Kod działa w zaledwie 2 sekundy. Dla przypomnienia, to właśnie otrzymuję, gdy modyfikuję rozmiar klawiszy tabeli przeglądowej (obejmuje wyniki drugiego uruchomienia bez resetowania modelu):

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

Interesujące byłoby wiedzieć, dlaczego kluczowy rozmiar 4 znaków jest najlepszym wyborem w tym algorytmie.

Porównanie tekstu

Oryginał:

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

Odtworzono:

"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 

Domysły:

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

Zmień dziennik

  • 569279 - zmieniono whale2.txti tym samym usunięto optymalizację.
  • 577366 - zoptymalizowany z kodem, który próbował zgadnąć, kiedy zwrócić wiersz.
  • 590354 - wersja oryginalna.

4
Dziękujemy za pokazanie wariancji podczas zmiany rozmiaru klucza i progu kolumny!
Jeremy Weirich,

Zaktualizowałem pytanie o wersję pliku, w której tekst nie jest zawijany - prawdopodobnie możesz zapisać niektóre punkty, używając tego
Nathaniel

@Nanielski rzeczywiście tak jest. Zaktualizowałem odpowiedź.
Charlie

Możesz zapisać niektóre bajty, używając var zamiast deklarować typy.
Ed T

1
W miarę zwiększania się wielkości klucza liczba trafień w nieudanych trafień będzie się zmniejszać, a zatem pojawi się więcej spacji, gdy krótszy klucz mógł odgadnąć właściwy znak. W miarę zmniejszania się rozmiaru klucza poszczególne domysły są mniej dokładne dla pasujących segmentów. Podejrzewam, że właśnie dlatego długość czterech jest optymalna. Jeśli utrzymałeś klucze o wielu długościach i użyłeś krótszych dopasowań, gdy dłuższe nie są dostępne, spodziewam się, że współczynnik trafień (a tym samym wynik) zostanie znacznie poprawiony przy dłuższych kluczach.
Jeffrey L. Whitledge

11

Java 7, 1995 znaków, (1995 * 2 + 525158) 529148

Java jest do bani dla małych rozmiarów programów. W każdym razie wypróbowałem kilka niezwykle złożonych i podchwytliwych podejść, które przyniosły zaskakująco badziewne rezultaty. Potem wróciłem i po prostu zastosowałem proste podejście, które spowodowało mniejszy rozmiar programu i lepsze wyniki.

To podejście jest w rzeczywistości niezwykle proste. Ślepo podaje poprzednie znaki x (oprócz wszystkich podciągów tych znaków) do tablicy mieszającej, odwzorowanej na bieżący znak. Następnie śledzi, które wzory najdokładniej przewidują obecną postać. Jeśli wzorce poprzedzające określone znaki występują wielokrotnie, udaje im się przewidzieć postać. Daje pierwszeństwo dłuższym ciągom i daje pierwszeństwo każdemu znakowi, który najczęściej występuje po danym ciągu. Ten algorytm nie wie nic o rodzaju dokumentu ani języku angielskim.

Postanowiłem użyć 9 znaków i próbować dopasować całe słowa w tych poprzednich 9 znakach, jeśli to możliwe. Gdy nie próbujesz dopasowywać słów w łańcuchach, optymalna długość to 6 znaków, co powoduje kilka tysięcy nieporozumień.

Ciekawym spostrzeżeniem było to, że użycie 20 znaków spowodowało złe przewidywania za pierwszym razem, ale 99,9 procent dokładności przy kolejnych podaniach. Algorytm w zasadzie był w stanie zapamiętać książkę w nakładających się na siebie 20 bajtowych fragmentach, a było to na tyle wyraźne, że mogło przywołać całą książkę na raz.

  • (1950 * 2 + 532919) 536819
  • (2406 * 2 + 526233) 531045 sprawdzanie interpunkcji w celu lepszego odgadywania
  • (1995 * 2 + 525158) 529148 więcej poprawek, grałem w golfa trochę gadatliwości

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

To całkiem niezły wynik jak na taki pełny język.
DJMcMayhem

1
Uznałem, że warto było spróbować, ponieważ rozmiar pliku daje dużo miejsca na ulepszenia w porównaniu do rozmiaru programu.
Jim W

3
Nie można tego skompilować pod Javą 7 (ani żadną wersją Java, o ile jest warta). Czy mógłbyś naprawić swój kod? Gdy to zrobisz, chętnie zagram w golfa, aby poprawić twój wynik.
Olivier Grégoire,

Nie przetestowano, ale powinien to być twój dokładnie ten sam kod lekko golfowy: 950 bajtów . Twój obecny kod zawierał jednak sporo błędów, więc nie jestem pewien, czy wszystko poprawnie wypełniłem. Ponownie, niesprawdzone, więc po prostu porównaj wersje, aby zobaczyć, co zmieniłem / zmieniłem nazwę i sprawdź, czy wszystko nadal działa tak samo jak oryginalny kod. Jednak zdecydowanie można jeszcze grać w golfa.
Kevin Cruijssen

Cholera, zrobiłem to, znudzony starą pracą i nie zabrałem ze sobą kodu. Muszę rzucić na to okiem, żeby zobaczyć, gdzie jest literówka.
Jim W

10

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

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

Próbowałem tego niezależnie, ale skończyło się na tym, że faktycznie jest to gorsza wersja odpowiedzi Michaela Homera. Mam nadzieję, że to nie czyni mojej odpowiedzi całkowicie nieaktualną.

Z czasem buduje się słownik słów (zdefiniowany jako ciągi znaków zakończone przez lub \n, z rozróżnianiem wielkości liter i włączając interpunkcję). Następnie przeszukuje ten słownik pod kątem słów zaczynających się od tego, co do tej pory wie o bieżącym słowie, sortuje wynikową listę według częstotliwości występowania (powoli) i zgaduje, że następny znak jest następnym znakiem w najczęściej pasującym słowie. Jeśli mamy już najczęściej pasujące słowo lub nie istnieje już pasujące słowo, zwraca ono .

Tworzy również obrzydliwie nieefektywny słownik par słów. Po osiągnięciu granicy słowa zgaduje, że następny znak jest pierwszą literą drugiego słowa w najczęściej pasującej parze słów lub tjeśli nie ma dopasowania. Nie jest to jednak zbyt mądre. Następnie Mobyprogram poprawnie zgaduje, że jest następny znak D, ale potem zapomina o kontekście i zwykle nazywa wieloryba „Kaczką Moby” (ponieważ słowo „holenderski” wydaje się częściej w pierwszej połowie tekstu ). Łatwo byłoby to naprawić, ustawiając pary słów na pojedyncze słowa, ale spodziewam się, że zysk byłby marginalny (ponieważ zwykle jest poprawny od trzeciego znaku, a pary słów nie są tak pomocne).

Mógłbym dostroić to, aby lepiej pasowało do dostarczonego tekstu, ale nie sądzę, aby ręczne dostrojenie algorytmu na podstawie wcześniejszej wiedzy na temat danych wejściowych było naprawdę w duchu gry, więc poza wybraniem t jako zastępczego znaku po spacji ( i prawdopodobnie też nie powinienem był tego robić), unikałem tego. Zignorowałem znaną długość linii pliku wejściowego i zamiast tego wstawiłem\n co każde 13 spacji - jest to prawie na pewno bardzo słabe dopasowanie, głównym celem było utrzymanie rozsądnej długości linii, a nie dopasowanie do danych wejściowych.

Kod nie jest dokładnie szybki (~ 2 godziny na mojej maszynie), ale ogólnie dostaje około połowy prawidłowych znaków (49%). Spodziewam się, że wynik byłby marginalnie lepszy, gdyby został uruchomiony whale2.txt, ale tego nie zrobiłem.

Początek danych wyjściowych wygląda następująco:

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

ale pod koniec wygląda trochę bardziej jak ... coś. Mój ulubiony fragment z końca książki

a ponieważ żadna z nich nie może być moja, pozwólcie mi następnie holować na kawałki, goniąc cię, choć przywiązany do ciebie, przeklęty wielorybie! TO oddaję włócznię! ”

wychodzi jako

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 "

To sprawiłoby, że Gniew Khana byłby znacznie bardziej zagmatwany. A „samotny” → „mrowienie” jest szczególnie satysfakcjonującym zamiennikiem.

Edycja: Zapisano jeden bajt, usuwając niepotrzebne miejsce

Punktacja

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

Spowoduje to uruchomienie programu dla tekstu Moby Dicka i wyprowadzenie „przewidywanego” tekstu na standardowe wyjście, i nadużywa stderr do napisania partytury. Polecam przekierowanie wyjścia do pliku.


2
Witamy w PPCG!
Martin Ender,

1
Czy nie lambda i:i[1]byłoby taniej niż radzenie sobie operator?
Draconis

@Draconis Prawie na pewno.
georgewatson

9

C ++, 2 62829 + 318786 = 444444

Aby uruchomić ten program, potrzebujesz tego pliku tutaj , który musi mieć nazwę C.

Program wykorzystuje tę samą kombinację modeli Markowa, co w naszej wcześniejszej odpowiedzi . Tak jak poprzednio, ta kombinacja jest zasadniczo modelem z tej odpowiedzi użytkownika 2699 , ale z kilkoma małymi modyfikacjami.

Zważywszy na to, że ta odpowiedź wykorzystuje dokładnie ten sam model, co poprzednio, ulepszenie jest lepszym mechanizmem teoretycznym niż opisany wcześniej mechanizm „przewijania”. Pozwala to popełnić mniej błędów, a jednocześnie ma mniejszą łączną długość. Sam program nie jest bardzo golfowy, ponieważ nie jest głównym czynnikiem wpływającym na wynik.

Program ma długość 2167 bajtów (w tym wszystkie zakładki wcięcia i wiele innych niepotrzebnych znaków, ale przed kodem testowym), a plik binarny Cma długość 60661 bajtów, więc zgodnie z zasadami oceniania wielu plików oceniamy Ljako 2167 + 60661 + 1 = 62829.

Uruchomienie programu na m5.4xlargeinstancji w Amazon EC2 zajmuje około 8 minut i zużywa nieco ponad 16 GB pamięci. (To nadmierne zużycie pamięci nie jest konieczne - po prostu też tego nie zoptymalizowaliśmy).

#include <map>
#include <queue>
#include <vector>
using namespace std;

FILE *in;
unsigned int a, b = -1, c, d;
string s, t;
double l, h = 1, x[128][129], y[129], m[128];
map<string, int> N;
map<string, double[128]> M;
int G, S;

int f(int C)
{
    int i, j;
    for (i = 0; i <= 20 && i <= S; i++) {
        t = s.substr(S - i);
        N[t]++;
        M[t][C]++;
    }
    s += C;
    S++;

    for (i = 0; i < 128; i++)
        m[i] = 0;

    int E = 0;
    for (i = 20; i >= 0; i--) {
        if (i > S)
            continue;
        t = s.substr(S - i);
        if (i <= 2 && E >= 100 && (i == 0 || t[0] != ' '))
            break;
        if (M.find(t) == M.end())
            continue;
        for (j = 0; j < 128; j++) {
            m[j] += M[t][j] / N[t];
        }
        E += N[t];
    }

    double r = 0;
    for (i = 0; i < 128; i++)
        r += m[i];
    for (i = 0; i < 128; i++)
        m[i] = m[i] / r;

    if (!in) {
        in = fopen("C", "r");
        for (i = 0; i < 4; i++)
            c = c << 8 | getc(in);
    } else {
        l = x[C][G]
            + (l - y[G]) * (x[C][G + 1] - x[C][G]) / (y[G + 1] - y[G]);
        h = x[C][G]
            + (h - y[G]) * (x[C][G + 1] - x[C][G]) / (y[G + 1] - y[G]);
    }

    priority_queue<pair<double, int>> q;
    for (i = 0; i < 128; i++) {
        q.push(make_pair(m[i], i));
    }

    int n = 0;
    double s = 0;
    while (q.size()) {
        i = q.top().second;
        q.pop();
        if (m[i] < s / (n + 15))
            break;
        s += m[i];
        n++;
    }

    r = 0;
    for (i = 0; i < 128; i++) {
        y[i + 1] = m[i] - s / (n + 15);
        if (y[i + 1] < 0)
            y[i + 1] = 0;
        r += y[i + 1];
    }
    for (i = 0; i < 128; i++)
        y[i + 1] /= r;

    for (i = 0; i < 128; i++) {
        r = 0;
        for (j = 0; j < 128; j++) {
            x[i][j + 1] = y[j + 1];
            if (i == j)
                x[i][j + 1] *= 16;
            r += x[i][j + 1];
        }
        for (j = 0; j < 128; j++)
            x[i][j + 1] /= r;
        x[i][0] = 0;
        for (j = 0; j < 128; j++)
            x[i][j + 1] += x[i][j];
    }

    y[0] = 0;
    for (i = 0; i < 128; i++)
        y[i + 1] += y[i];

    for (G = 0; G < 128; G++) {
        if (y[G + 1] <= l)
            continue;
        if (y[G + 1] < h) {
            d = a + (b - a) * ((h - y[G + 1]) / (h - l));
            if (c <= d) {
                b = d;
                l = y[G + 1];
            } else {
                a = d + 1;
                h = y[G + 1];
            }
            while ((a ^ b) < (1 << 24)) {
                a = a << 8;
                b = b << 8 | 255;
                c = c << 8 | getc(in);
            }
        }
        if (h <= y[G + 1])
            return G;
    }
}
// End submission here.  Test code follows.
int main()
{
    FILE *moby = fopen("whale2.txt", "r");

    int E = 0;
    int c = getc(moby);
    while (c != EOF) {
        int guess = f(c);
        c = getc(moby);
        if (c != guess)
            E++;
    }

    printf("E=\t%d\n", E);

    return 0;
}

7

Python 3, 526640

274 bajty, 526092 błędy (za pomocą whale2.txt). To z pewnością jest w stanie poprawić, ale osiągnął etap „wystarczająco dobry, aby opublikować”.

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

Chodzi o to, aby zapisać częstotliwości wszystkich przebiegów 2, 3, 4, ..., 10 znaków. Dla każdej z tych długości L sprawdzamy, czy najnowsze znaki L-1 pasują do zapisanego wzoru; jeśli tak, to zgadujemy, że L jest najczęstszą kolejną postacią stosowaną według tego wzoru. W ten sposób zbieramy do dziewięciu domysłów. Aby zdecydować, której zgadnąć użyć, ważymy częstotliwość każdego wzoru według jego długości do 8. potęgi. Wybrano przypuszczenie o największej sumie ważonych częstotliwości. Jeśli nie ma pasujących wzorów, domyślamy się miejsca.

(Maksymalna długość wzoru i wykładnik ważenia zostały wybrane metodą prób i błędów, aby dać najmniej błędnych domysłów.)

Oto moja niezakończona wersja w toku:

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

I uprząż testowa:

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

Oto przykładowe dane wyjściowe z początku tekstu. Już zaczynamy dostrzegać możliwości, aby zakończyć wspólne słowa po obejrzeniu swój pierwszy list ( in, to, and, by, również, jak widać,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 

Pod koniec wciąż jest mnóstwo błędów, ale także mnóstwo bardzo dobrych sekwencji ( shmage seashawksna przykład).

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

Interesujące jest spojrzenie na niektóre błędy i odgadnięcie, jakiego słowa algorytm „oczekiwał”. Na przykład, gdy sailprogram oba razy przewiduje o--for sailor, jak sądzę. Lub ponownie, gdy , asię spodziewa - nprawdopodobnie z powodu powszechnego występowania, and .


Dziennik zmian:

  • 274 * 2 + 526092 = 526640 Algorytm poddał golfowi kosztem kilku dodatkowych błędów
  • 306 * 2 + 526089 = 526701 Wersja oryginalna

6

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

Wyszukuje słowa pasujące do poprzednich znaków z listy  wszystkich  większości słów użytych w tekście, posortowanych według liczby ich wystąpienia.

Kod:

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"

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

Zestaw testowy:

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

Edycja: Używanie whale2.txtdaje lepszy wynik.


5

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

Jeszcze jedno przedłożenie częstotliwości. Uruchom whale2.txti uzyskaj podobny (nieco gorszy) wynik niż inni.

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

Ten łapczywie odnajduje najdłuższy ciąg, który zaczyna się od sufiksu historii, a jeśli jest wielu kandydatów, rozstaje się z krótszymi ciągami.

Na przykład: jeśli ostatnie 7 znaków to abcdefgh, a ciąg abcdefghii abcdefghjpojawia się z największą częstotliwością we wszystkich ciągach formularza abcdefgh*, wynikiem będzie albo, ialbo jtiebreak z krótszymi przyrostkami ( bcdefgh,cdefgh , ...).

Z nieznanych przyczyn cokolwiek więcej niż 7, a mój komputer nie ma wystarczającej ilości pamięci RAM, aby go uruchomić. Nawet w przypadku wersji 7 muszę zamknąć wszystkie przeglądarki internetowe, aby go uruchomić.


Kod testowy:

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

Nie golfowany:

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

Przykładowe dane wyjściowe:

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

Ten jest pod koniec tekstu. Większość długie słowa są dość dokładnie przewidzieć ( 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

Wielkie litery nie wydają się dobre.


Trie wydaje się zajmować więcej pamięci, niż się spodziewałem ...
user202729 14.01.2018

... a także trudniejsze do wdrożenia.
user202729

4

Python 2, 756837

Używa czegoś, co może być łańcuchem Markowa?

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
Szybkie wyjaśnienie: zlib.decompress('...')Ocenia {'G?':' ', 'G;':' ','G"':' ',.......}i ajest słownikiem, który odwzorowuje od 2 znaków do 1 znaku. Zasadniczo wariant 2-charakter Steadybox za odpowiedź .
user202729,

1
Jak widzę, literał ma 17780 bajtów. Można go zmniejszyć do 11619 znaków, usuwając białe spacje w zdekompresowanej treści, co pozwala zaoszczędzić 12322 bajtów. (jeśli poprawnie policzyłem) Również ... konwersja kodów ucieczki heksadecymalnych na rzeczywiste nieprzetworzone znaki może zaoszczędzić jeszcze więcej bajtów.
user202729,

Jak opublikować tutaj coś, jeśli jest to nieprzetworzony bajt?
Skyler

1
xxd, hexdump, uuencode, Lub podobny
Peter Taylor

@ user202729 Pamiętaj, że kod Pythona nie może zawierać rzeczywistych nieprzetworzonych bajtów 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)

Przykład:

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

Wykorzystuje trzy wstępnie obliczone pliki pomocnicze:

  • seqwords zawiera 236 najczęstszych słów.
  • wordsseq zawiera skompresowany przez LZMA ciąg tych słów i dla wszystkich słów nie należących do 236 najczęściej, długość.
  • dicttrieszawiera, dla każdej długości słowa, drzewo decyzyjne, które zawiera wszystkie pozostałe słowa. Z tych prób wpisy są wybierane na bieżąco.

W ten sposób osiągamy znacznie niższy poziom błędu niż wszystkie inne schematy stratne; Niestetywordsseq plik jest wciąż zbyt duży, aby był konkurencyjny.

Oto ukończona wersja, która tworzy pliki i wykonuje analizę:

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;

Na obecnym etapie rozwiązaniem jest WIP i dlatego nie jest golfem. Biorąc również pod uwagę fakt, że rzeczywisty rozmiar kodu nie ma prawie żadnego wpływu na wynik, pomyślałem, że najpierw opublikuję swoją odpowiedź, zanim zacznę ją mikrooptymalizować.
(Pełny kod dostępny tutaj: https://github.com/BrainStone/MobyDickRNG - Obejmuje pełne wyszukiwanie programu i nasion)

To rozwiązanie jest oparte na RNG. Najpierw analizuję tekst. Tworzę mapę, która liczy wystąpienia dwóch następujących po sobie postaci. Następnie tworzę mapę dystrybucji. Wszystko to odbywa się statycznie, więc powinno być zgodne z zasadami.

Następnie, próbując wydrukować tekst, sprawdzam i wyciągam losowy znak z możliwych. Chociaż zwykle daje to gorsze wyniki niż tylko wypisywanie najpopularniejszej następującej litery, mogą istnieć boga ziarna, które przyniosą lepsze wyniki. Dlatego ziarno jest mocno zakodowane. Obecnie szukam najlepszego ziarna. Zaktualizuję tę odpowiedź, gdy znajdę lepsze nasiona. Bądź na bieżąco!

Jeśli ktoś chce samodzielnie szukać nasion lub korzystać z różnych RNG, nie wahaj się rozwiązywać repozytorium.

Metoda zastosowana do obliczenia wyniku: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15

Zauważ, że chociaż całkowity wynik jest w tej chwili najgorszy, to przewyższa liczbę błędów po prostu wypisując spacje. Są duże szanse, że wynik spadnie, sprawdzając więcej nasion.

Dziennik zmian

  • 2018/01/24 : Wysłano wstępną odpowiedź.
    Nasiona w kratkę: 0-50000. Wynik: 2305 * 2 + 1017754 = 1022364
  • 2018/01/24 : Uprawiałem trochę golfa. Dodano link do metody obliczania wyniku.
    Nasiona w kratkę: 0–80000. Wynik: 1920 * 2 + 1017754 = 1021594 (-770)
  • 2018/02/02 : Nowe ziarno (10087702) (nie znalazłem czasu na poprawienie przesłania)
    Sprawdzone nasiona: 0-32000000. Wynik: 1923 * 2 + 1017344 = 1021190 (-404)

Czy możesz dołączyć do odpowiedzi uprząż testową, która ocenia wynik?
Nathaniel

@Nanielaniel Powiązałem kod wyniku bezpośrednio. Czy oprócz repozytorium uważasz to za wystarczające?
BrainStone

Po przejrzeniu zasad zauważyłem, że naruszam niektóre z nich. Naturalnie zaktualizuję swoją odpowiedź, gdy naprawię problemy
BrainStone

Skończysz wtedy kodowaniem tekstu w losowym ziarnie. Zobacz ezoteryczny język programowania Seed , a może zechcesz odtworzyć program MT19937 i pokonać tę odpowiedź (jeśli możesz).
user202729

Fajny pomysł, ale nie pomoże uzyskać dobrego wyniku. W każdym razie +1.
user202729

3

Ruby, 1164418 (ouch)

Chciałem tylko zobaczyć, jak sobie radzę bez sprawdzania innych odpowiedzi.
Nie jestem pewien, czy jest to dozwolone, ponieważ zawiera literał, który wygenerowałem analizując plik, ale nawet jeśli tak nie było, groziło to komukolwiek.

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

Jak wygenerowałem x

Najpierw wygenerowałem a.txtz następującymi:

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

Następnie wygenerowałem a.csv:

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

Następnie przeanalizowałem go xza pomocą następującego skryptu 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

Jak zdobyłem

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

Jestem pewien, że to dozwolone; jeśli jesteś analizowany plik, dobra robota! Tylko jeśli program to robi, jest to nieprawidłowe.
Erik the Outgolfer

@EriktheOutgolfer> _> (po cichu wsuwa „(nie konkuruje)” do tytułu)
NO_BOOT_DEVICE

Dlaczego? Jeśli jest to ważne, to konkuruje, chociaż może nie pobić dużo. Jeśli jest niepoprawny (to znaczy, twoje rozwiązanie czyta z pliku i nie zawiera tylko literału), należy je usunąć.
Erik the Outgolfer

Hmmm. Myślałem, że masz na myśli, jeśli jakiś program przeanalizuje plik, a nie tylko rozwiązanie.
NO_BOOT_DEVICE

1
Nie umiem czytać Rubiego, ale myślę, że to jest poprawne. Posiadanie dosłowności w programie jest całkowicie w porządku, nie stanowi żadnego problemu.
Nathaniel,

2

Python 3 , (146 * 2 + 879757) 880049 bajtów

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

Wypróbuj online!

Dość prosta tabela częstotliwości. Każda pozycja w ciągu odpowiada kodowi ascii bieżącego znaku (minus 10 = 0x0a = „\ n”, najniższy znak w pliku), a znak przy każdym indeksie jest najczęstszym następnym znakiem. Zakładając, że poprawnie wyliczyłem częstotliwości…

Przetestowano za pomocą kodu z testu user202729


Czy możesz zapisać niektóre bajty, używając def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]?
Neil

0

[Python 3] (644449 * 2 + 0) 1288898 punktów

Doskonała dokładność w zaledwie 644449 bajtów

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

Pełny kod nie mieści się w odpowiedzi, więc umieściłem go tutaj i zastąpiłem literał dużego ciągu binarnego literą b '###' w tekście odpowiedzi.

Jest on generowany za pomocą następującego kodu, w którym „zmodyfikowany.py” to wygenerowany plik, a „cheatsheet.txt” to plik whale2.txt rozpoczynający się od drugiego znaku.

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

Kod można wykonać, dodając następujący tekst na końcu „Modified.py”. „whale2.txt” musi znajdować się w tym samym katalogu co „zmodyfikowany.py”, a dane wyjściowe zostaną zapisane w „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)

Ta odpowiedź nie zapewnia bezpośredniego dostępu do pliku whale.txt lub whale2.txt. Korzysta z istniejących standardowych bibliotek kompresji, co jest wyraźnie dozwolone w regułach.


może tam być „\ r \ n”, którego nie mogłem się pozbyć w systemie Windows, kiedy je liczyłem
Legorhin

2
tak, to była literówka, która się propagowała
Legorhin
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.