Viết Moby Dick, khoảng


297

Dưới đây là tệp văn bản ASCII 1,2Mb chứa văn bản của Moby-Dick của Herman Melville ; hoặc, Cá voi . Nhiệm vụ của bạn là viết một chương trình hoặc hàm (hoặc lớp, v.v. - xem bên dưới) sẽ được cung cấp cho tệp này một ký tự một lần và tại mỗi bước phải đoán ký tự tiếp theo.

Đây là . Điểm của bạn sẽ là

2*L + E

nơi Llà kích thước của trình của bạn theo byte, và Elà số ký tự nó đoán không chính xác. Điểm thấp nhất sẽ thắng.

Cụ thể hơn

Nội dung gửi của bạn sẽ là một chương trình hoặc chức năng (v.v.) sẽ được gọi hoặc gọi hoặc gửi dữ liệu nhiều lần. (1215235 lần để được chính xác.) Khi nó được gọi là cho n th thời gian nó sẽ được cung cấp n th nhân vật của whale.txthay whale2.txtvà nó phải ra dự đoán của mình cho ( n + 1 ) lần thứ nhân vật. Thành Ephần điểm số của nó sẽ là tổng số ký tự mà nó đoán không chính xác.

Hầu hết các bài nộp sẽ cần lưu trữ một số trạng thái ở giữa các lệnh, để chúng có thể theo dõi số lần chúng được gọi và các đầu vào trước đó là gì. Bạn có thể làm điều này bằng cách ghi vào một tệp bên ngoài, bằng cách sử dụng statichoặc các biến toàn cục, bằng cách gửi một lớp chứ không phải là một hàm, sử dụng một trạng thái đơn vị hoặc bất cứ điều gì khác làm việc cho ngôn ngữ của bạn. Việc gửi của bạn phải bao gồm bất kỳ mã nào được yêu cầu để khởi tạo trạng thái của nó trước lần gọi đầu tiên.

Chương trình của bạn nên chạy một cách xác định, để nó luôn đưa ra những dự đoán giống nhau cho cùng một đầu vào (và do đó luôn có cùng số điểm).

Câu trả lời của bạn phải bao gồm không chỉ bài nộp của bạn, mà cả mã bạn đã sử dụng để tính Ephần của điểm. Điều này không cần phải được viết bằng cùng ngôn ngữ với nội dung bạn gửi và sẽ không được tính vào số byte của nó. Bạn được khuyến khích để làm cho nó có thể đọc được.

Về giao diện giữa trình của bạn và chương trình tính điểm này, mọi thứ đều ổn, miễn là chương trình của bạn luôn cung cấp một byte đầu ra trước khi nhận byte đầu vào tiếp theo. (Vì vậy, ví dụ, bạn không thể truyền cho nó một chuỗi chứa tất cả đầu vào và lấy lại chuỗi chứa tất cả đầu ra.)

Bạn thực sự phải chạy chương trình kiểm tra của bạn và tính toán / xác minh điểm số của bạn trước khi gửi bài dự thi của bạn. Nếu bài nộp của bạn chạy quá chậm để bạn xác minh điểm của mình thì nó không đủ điều kiện để cạnh tranh, ngay cả khi bạn biết điểm của nó về nguyên tắc là bao nhiêu.

Thành Lphần của điểm số của bạn sẽ được tính theo các quy tắc thông thường cho các thử thách golf mã. Nếu bài gửi của bạn sẽ chứa nhiều tệp, vui lòng lưu ý các quy tắc về tính điểmcấu trúc thư mục trong trường hợp đó. Bất kỳ dữ liệu mà mã của bạn sử dụng phải được bao gồm trong Lđiểm số của bạn .

Bạn có thể nhập các thư viện hiện có nhưng không thể tải bất kỳ tệp bên ngoài nào khác và mã của bạn có thể không truy cập vào whale.txthoặcwhale2.txttập tin theo bất kỳ cách nào khác hơn là mô tả ở trên. Bạn không được tải bất kỳ mạng thần kinh được đào tạo trước hoặc các nguồn dữ liệu thống kê khác. (Sử dụng mạng thần kinh là tốt, nhưng bạn phải bao gồm dữ liệu trọng lượng trong bài gửi của mình và tính nó theo số byte của bạn.) Nếu vì lý do nào đó, ngôn ngữ hoặc thư viện của bạn bao gồm một tính năng cung cấp một số hoặc tất cả văn bản của Moby Dick , bạn không thể sử dụng tính năng đó. Ngoài ra, bạn có thể sử dụng bất kỳ tính năng thư viện hoặc tích hợp nào khác mà bạn thích, bao gồm các tính năng liên quan đến xử lý văn bản, dự đoán hoặc nén, miễn là chúng là một phần của ngôn ngữ hoặc thư viện chuẩn của bạn. Đối với các thói quen kỳ lạ, chuyên biệt hơn bao gồm các nguồn dữ liệu thống kê, bạn sẽ phải tự thực hiện chúng và đưa chúng vào số byte của bạn.

Có khả năng một số bài nộp sẽ bao gồm các thành phần được tạo bởi mã. Nếu đây là trường hợp, vui lòng bao gồm trong câu trả lời của bạn mã được sử dụng để sản xuất chúng và giải thích cách thức hoạt động của nó . (Miễn là mã này không cần thiết để chạy trình của bạn, nó sẽ không được bao gồm trong số byte của bạn.)

Vì lý do lịch sử, có hai phiên bản của tệp và bạn có thể sử dụng một trong hai phiên bản đó trong câu trả lời. Trong whale2.txt(được liên kết ở trên) văn bản không được bọc, vì vậy các dòng mới chỉ xuất hiện ở cuối đoạn văn. Trong bản gốc whale.txt, văn bản được gói thành chiều rộng 74 ký tự, vì vậy bạn phải dự đoán kết thúc của mỗi dòng cũng như dự đoán văn bản. Điều này làm cho thách thức khó khăn hơn, vì vậy whale2.txtđược khuyến nghị cho câu trả lời mới. Cả hai tệp có cùng kích thước, 1215236 byte.


Để tóm tắt, tất cả các câu trả lời nên bao gồm những điều sau đây:

  • Trình của bạn chính nó. (Mã, cộng với bất kỳ tệp dữ liệu nào nó sử dụng - đây có thể là các liên kết nếu chúng lớn.)
  • Một lời giải thích về cách mã của bạn hoạt động. Vui lòng giải thích phương pháp I / O cũng như cách dự đoán ký tự tiếp theo. Giải thích về thuật toán của bạn rất quan trọng và những giải thích tốt sẽ kiếm được tiền thưởng từ tôi.
  • Mã bạn đã sử dụng để đánh giá điểm số của bạn. (Nếu đây là giống hệt với câu trả lời trước đó, bạn chỉ có thể liên kết với nó.)
  • Bất kỳ mã nào bạn đã sử dụng để tạo trình của mình, cùng với lời giải thích về mã đó. Điều này bao gồm mã mà bạn đã sử dụng để tối ưu hóa các tham số, tạo tệp dữ liệu, v.v. (Điều này không được tính vào số byte của bạn nhưng nên được đưa vào câu trả lời của bạn.)

Bảng xếp hạng

Tiền thưởng

Thỉnh thoảng tôi sẽ cung cấp tiền thưởng để khuyến khích các cách tiếp cận khác nhau.

Người đầu tiên, 50 điểm, được trao cho A. Rex cho câu trả lời đạt điểm cao nhất vào thời điểm đó.

Thứ hai, 100 điểm, cũng được trao cho A. Rex, cho cùng một câu trả lời, bởi vì họ đã thêm một lời giải thích rất tốt cho câu trả lời hiện có của họ.

Tiền thưởng tiếp theo, 200 điểm , sẽ được trao cho một trong hai

  • Một câu trả lời cạnh tranh sử dụng một kỹ thuật mới. (Điều này sẽ dựa trên đánh giá chủ quan của tôi vì đó là đại diện của tôi đi vào tiền thưởng, nhưng bạn có thể tin tưởng tôi là công bằng. Lưu ý rằng câu trả lời của bạn cần có đủ lời giải thích cho tôi để hiểu cách thức hoạt động của nó!) Không đạt điểm cao nhất, nó chỉ cần làm tốt một cách hợp lý so với các câu trả lời hiện có. Tôi đặc biệt muốn thấy các giải pháp dựa trên các mạng thần kinh tái phát, nhưng tôi sẽ thưởng tiền thưởng cho bất cứ thứ gì có vẻ khác biệt so với các mô hình Markov thống trị điểm số cao nhất hiện tại.

Hoặc là:

  • Bất cứ ai khác đánh bại điểm số cao nhất của A. Rex (hiện tại là 444444), sử dụng bất kỳ phương pháp nào.

Khi tiền thưởng 200 điểm được yêu cầu, rất có thể tôi sẽ cung cấp 400 điểm một, cập nhật các yêu cầu tương ứng.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Dennis

9
xkcd.com/1960 dường như là một tài liệu tham khảo cho thử thách này!
A. Rex

Tôi đã nghĩ đến việc nén cái này ... nhưng hơi lâu khi máy tính của tôi bị hỏng nhún
Naruyoko

Câu trả lời:


135

/// , 2 * 1 + 1020874 = 1020876

 

In một khoảng trắng.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Dennis

Đó là một số hack phần thưởng cực kỳ thông minh! Bạn phải là một AGI;)
Alex

97

Node.js, 2 * 224 + 524279 = 524727

Vui lòng tham khảo nhật ký thay đổi ở cuối bài này để cập nhật điểm.

Một hàm lấy và trả về một byte.

a=[...l='14210100'],m={},s={},b={}
f=c=>a.some((t,n)=>x=s[y=l.slice(n)]>t|/^[A-Z '"(]/.test(y)&&b[y],l+=String.fromCharCode(c),a.map((_,n)=>(m[x=l.slice(n)]=-~m[x])<s[y=l.slice(n,8)]||(s[y]=m[x],b[y]=c)),l=l.slice(1))&&x||32

Nó bao gồm một mô hình PPM đơn giản , nhìn vào 8 ký tự cuối cùng để dự đoán nhân vật tiếp theo.

Chúng tôi tin tưởng một mẫu có độ dài L khi chúng tôi đã gặp nó ít nhất T [L] lần, trong đó T là một mảng các ngưỡng tùy ý: [1,1,2,1,2,3,5,2] . Hơn nữa, chúng tôi luôn tin tưởng một mô hình có nhân vật đầu tiên phù hợp [A-Z '"(].

Chúng tôi chọn mẫu đáng tin cậy dài nhất và trả về dự đoán có số điểm cao nhất liên quan đến mẫu này tại thời điểm cuộc gọi.

Ghi chú

  • Điều này rõ ràng là không được tối ưu hóa cho tốc độ, nhưng nó chạy trong khoảng 15 giây trên máy tính xách tay của tôi.

  • Nếu chúng ta được phép lặp lại quá trình nhiều lần liên tiếp mà không đặt lại mô hình, số lỗi sẽ hội tụ đến ~ 268000 sau 5 lần lặp.

  • Tỷ lệ thành công hiện tại của chức năng dự đoán là ~ 56,8%. Như @immibis nhận thấy trong các bình luận, nếu các dự đoán xấu và chính xác được trộn lẫn với nhau, kết quả thậm chí không thể đọc được.

    Chẳng hạn, đoạn trích này ở gần cuối cuốn sách:

    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.
    

    trở thành:

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

    Bằng cách thay thế các dự đoán xấu bằng dấu gạch dưới, chúng ta có một ý tưởng tốt hơn về những gì chức năng đã đúng:

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

    NB : Ví dụ trên được tạo với phiên bản mã trước đó, hoạt động trên phiên bản đầu tiên của tệp đầu vào.

Mã kiểm tra

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

Thay đổi nhật ký

  • 524727 - đã lưu được 19644 điểm bằng cách chuyển sang whale2.txt (cập nhật thử thách)
  • 544371 - đã lưu 327 điểm bằng cách buộc các mẫu bắt đầu bằng chữ in hoa, trích dẫn, trích dẫn kép hoặc dấu ngoặc đơn mở luôn luôn được tin cậy
  • 544698 - đã lưu 2119 điểm bằng cách buộc các mẫu bắt đầu bằng một khoảng trắng luôn được tin cậy
  • 546817 - đã lưu 47 điểm bằng cách điều chỉnh các ngưỡng và đánh gôn chức năng dự đoán
  • 546864 - đã lưu 1496 điểm bằng cách mở rộng độ dài mẫu tối đa thành 8 ký tự
  • 548360 - đã lưu 6239 điểm bằng cách đưa ra khái niệm về các mẫu đáng tin cậy, với các ngưỡng tùy thuộc vào độ dài của chúng
  • 554599 - đã lưu 1030 điểm bằng cách cải thiện dự đoán nguồn cấp dữ liệu
  • 555629 - lưu 22 điểm bằng cách chơi golf chức năng dự đoán
  • 555651 - tiết kiệm 40 điểm bằng cách chơi golf chức năng dự đoán
  • 555691 - điểm ban đầu

44
Đối với những người tò mò, không, điều này không tạo ra bất cứ thứ gì như Moby Dick. Đó là rất nhiều 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. Nó đôi khi quản lý để có được một vài từ hoàn chỉnh. Thích whales.
Immibis

23
@immibis Tiêu đề của thử thách đã được chọn một cách khôn ngoan. Đây là Moby Dick, khoảng . :-)
Arnauld

3
@Nathaniel Đã có nhiều cập nhật, vì vậy sẽ hầu như không thể đọc được và không thực sự nhiều thông tin. Tôi đã thêm một nhật ký thay đổi thay vì giải thích ngắn về các cải tiến.
Arnauld

45
Tôi nghĩ rằng chương trình của bạn thực sự đang thực hiện một bản dịch hoàn hảo sang tiếng Gaelic.
Beska

1
@ Draco18s Thật khó để biết dấu phẩy này là một dự đoán tốt hay xấu. Nếu đó là một dự đoán xấu, chức năng dự đoán có thể đã cố gắng đặt một lá thư một cách hợp pháp sau bất cứ thứ gì-khác-thư-đó-thực-sự-ở-đó-thay vì dấu phẩy một khi nó nhận được.
Arnauld

91

Perl, 2 · 70525 + 326508 = 467558

Dự đoán

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

Để chạy chương trình này, bạn cần tệp này ở đây , phải được đặt tên B. (Bạn có thể thay đổi tên tệp này trong trường hợp thứ hai của ký tự Bở trên.) Xem bên dưới để biết cách tạo tệp này.

Chương trình sử dụng kết hợp các mô hình Markov về cơ bản như trong câu trả lời này của user2699 , nhưng với một vài sửa đổi nhỏ. Điều này tạo ra một phân phối cho các nhân vật tiếp theo. Chúng tôi sử dụng lý thuyết thông tin để quyết định chấp nhận lỗi hay dành bit lưu trữ trong Bgợi ý mã hóa (và nếu có, làm thế nào). Chúng tôi sử dụng mã hóa số học để lưu trữ tối ưu các bit phân đoạn từ mô hình.

Chương trình dài 582 byte (bao gồm một dòng mới cuối cùng không cần thiết) và tệp nhị phân Bdài 69942 byte, vì vậy theo quy tắc cho điểm nhiều tệp , chúng tôi ghi điểm Llà 582 + 69942 + 1 = 70525.

Chương trình gần như chắc chắn đòi hỏi kiến ​​trúc 64 bit (chút cuối?). Mất khoảng 2,5 phút để chạy trên một m5.largephiên bản trên Amazon EC2.

Mã kiểm tra

# 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

Khai thác thử nghiệm giả định việc gửi trong tệp submission.pl, nhưng điều này có thể dễ dàng thay đổi trong dòng thứ hai.

So sánh văn bản

"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

Mẫu này (được chọn trong câu trả lời khác ) xảy ra khá muộn trong văn bản, vì vậy mô hình này khá phát triển vào thời điểm này. Hãy nhớ rằng mô hình được tăng thêm 70 kilobyte "gợi ý" trực tiếp giúp nó đoán các ký tự; nó không bị điều khiển đơn giản bởi đoạn mã ngắn ở trên.

Tạo gợi ý

Chương trình sau đây chấp nhận mã gửi chính xác ở trên (trên đầu vào tiêu chuẩn) và tạo Btệp chính xác ở trên (trên đầu ra tiêu chuẩn):

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

Phải mất khoảng thời gian dài để chạy như trình, vì nó thực hiện các tính toán tương tự.

Giải trình

Trong phần này, chúng tôi sẽ cố gắng mô tả những gì giải pháp này thực hiện đủ chi tiết để bạn có thể "tự thử tại nhà". Kỹ thuật chính phân biệt câu trả lời này với các câu hỏi khác là một vài phần dưới dạng cơ chế "tua lại", nhưng trước khi chúng ta đến đó, chúng ta cần thiết lập các điều cơ bản.

Mô hình

Thành phần cơ bản của giải pháp là một mô hình ngôn ngữ. Đối với mục đích của chúng tôi, một mô hình là một cái gì đó lấy một lượng văn bản tiếng Anh và trả về phân phối xác suất cho ký tự tiếp theo. Khi chúng tôi sử dụng mô hình, văn bản tiếng Anh sẽ là một số tiền tố (chính xác) của Moby Dick. Xin lưu ý rằng đầu ra mong muốn là một bản phân phối và không chỉ là một phỏng đoán duy nhất cho nhân vật có khả năng nhất.

Trong trường hợp của chúng tôi, về cơ bản, chúng tôi sử dụng mô hình trong câu trả lời này bởi user2699 . Chúng tôi không sử dụng mô hình từ các câu trả lời đạt điểm cao nhất (trừ riêng của chúng tôi) bởi Anders Kaseorg chính vì chúng tôi không thể trích xuất một phân phối chứ không phải là một đoán tốt nhất duy nhất. Về lý thuyết, câu trả lời đó tính toán một ý nghĩa hình học có trọng số, nhưng chúng tôi đã nhận được một số kết quả kém khi chúng tôi giải thích điều đó theo nghĩa đen. Chúng tôi "đánh cắp" một mô hình từ một câu trả lời khác vì "nước sốt bí mật" của chúng tôi không phải là mô hình mà là cách tiếp cận tổng thể. Nếu ai đó có mô hình "tốt hơn", thì họ sẽ có thể có kết quả tốt hơn bằng cách sử dụng các kỹ thuật còn lại của chúng tôi.

Một lưu ý, hầu hết các phương pháp nén như Lempel-Ziv có thể được coi là một "mô hình ngôn ngữ" theo cách này, mặc dù người ta có thể phải nheo mắt một chút. (Điều này đặc biệt khó khăn đối với thứ gì đó thực hiện chuyển đổi Burrows-Wheeler!) Ngoài ra, lưu ý rằng mô hình của user2699 là một sửa đổi của mô hình Markov; về cơ bản không có gì khác cạnh tranh cho thách thức này hoặc thậm chí có thể mô hình hóa văn bản nói chung.

Kiến trúc tổng thể

Với mục đích hiểu biết, thật tuyệt khi chia kiến ​​trúc tổng thể thành nhiều phần. Từ quan điểm cấp cao nhất, cần có một chút mã quản lý nhà nước. Điều này không đặc biệt thú vị, nhưng để hoàn thiện, chúng tôi muốn nhấn mạnh rằng tại mọi thời điểm, chương trình được yêu cầu đoán tiếp theo, nó có sẵn tiền tố chính xác của Moby Dick. Chúng tôi không sử dụng những dự đoán không chính xác trong quá khứ theo bất kỳ cách nào. Vì mục đích hiệu quả, mô hình ngôn ngữ có thể sử dụng lại trạng thái của nó từ N ký tự đầu tiên để tính trạng thái của nó cho các ký tự (N + 1) đầu tiên, nhưng về nguyên tắc, nó có thể tính toán lại mọi thứ từ đầu mỗi khi được gọi.

Chúng ta hãy đặt "trình điều khiển" cơ bản của chương trình sang một bên và lén nhìn vào phần đoán nhân vật tiếp theo. Về mặt khái niệm, nó giúp tách ba phần: mô hình ngôn ngữ (đã thảo luận ở trên), tệp "gợi ý" và "trình thông dịch". Ở mỗi bước, trình thông dịch sẽ yêu cầu mô hình ngôn ngữ phân phối cho ký tự tiếp theo và có thể đọc một số thông tin từ tệp gợi ý. Sau đó, nó sẽ kết hợp những phần này thành một dự đoán. Chính xác những thông tin trong tập tin gợi ý cũng như cách sử dụng nó sẽ được giải thích sau, nhưng bây giờ nó giúp giữ cho các phần này tách biệt về mặt tinh thần. Lưu ý rằng triển khai khôn ngoan, tệp gợi ý đúng nghĩa là một tệp (nhị phân) riêng biệt nhưng nó có thể là một chuỗi hoặc một cái gì đó được lưu trữ bên trong chương trình. Như một xấp xỉ,

Nếu một người đang sử dụng một phương pháp nén tiêu chuẩn, chẳng hạn như bzip2 như trong câu trả lời này , tệp "gợi ý" tương ứng với tệp nén. "Trình thông dịch" tương ứng với bộ giải nén, trong khi "mô hình ngôn ngữ" hơi ẩn (như đã đề cập ở trên).

Tại sao sử dụng một tập tin gợi ý?

Hãy chọn một ví dụ đơn giản để phân tích thêm. Giả sử rằng văn bản Ndài các ký tự và được xấp xỉ bằng một mô hình trong đó mỗi ký tự là (độc lập) chữ cái Ecó xác suất nhỏ hơn một nửa, Ttương tự với xác suất hơi nhỏ hơn một nửa và Avới xác suất 1/1000 = 0,1%. Giả sử không có nhân vật nào khác là có thể; trong mọi trường hợp, Anó khá giống với trường hợp của một nhân vật chưa từng thấy trước đây ra khỏi màu xanh.

Nếu chúng tôi hoạt động theo chế độ L 0 (hầu hết, nhưng không phải tất cả, các câu trả lời khác cho câu hỏi này), sẽ không có chiến lược nào tốt hơn cho người phiên dịch hơn là chọn một trong ET. Trung bình, nó sẽ nhận được khoảng một nửa số ký tự chính xác. Vậy E N / 2 và điểm N / 2 cũng vậy. Tuy nhiên, nếu chúng ta sử dụng chiến lược nén, thì chúng ta có thể nén đến ít hơn một bit cho mỗi ký tự. Vì L được tính bằng byte, chúng tôi nhận được L ≈ N / 8 và do đó điểm N / 4, tốt gấp đôi so với chiến lược trước đó.

Đạt được tỷ lệ này ít hơn một bit cho mỗi ký tự cho mô hình này là không cần thiết, nhưng một phương pháp là mã hóa số học.

Mã hóa số học

Như thường được biết đến, mã hóa là một cách biểu diễn một số dữ liệu bằng cách sử dụng bit / byte. Ví dụ, ASCII là mã hóa 7 bit / ký tự của văn bản tiếng Anh và các ký tự liên quan và nó là mã hóa của tệp Moby Dick gốc đang được xem xét. Nếu một số chữ cái phổ biến hơn các chữ cái khác, thì mã hóa có chiều rộng cố định như ASCII là không tối ưu. Trong tình huống như vậy, nhiều người tiếp cận với tiền mã hóa Huffman . Điều này là tối ưu nếu bạn muốn một mã cố định (không có tiền tố) với số bit nguyên cho mỗi ký tự.

Tuy nhiên, mã hóa số học thậm chí còn tốt hơn. Nói một cách đơn giản, nó có thể sử dụng các bit "phân đoạn" để mã hóa thông tin. Có rất nhiều hướng dẫn để mã hóa số học có sẵn trực tuyến. Chúng ta sẽ bỏ qua các chi tiết ở đây (đặc biệt là việc triển khai thực tế, có thể hơi rắc rối từ góc độ lập trình) vì các tài nguyên khác có sẵn trực tuyến, nhưng nếu ai đó phàn nàn, có lẽ phần này có thể được bổ sung thêm.

Nếu một người có văn bản thực sự được tạo bởi một mô hình ngôn ngữ đã biết, thì mã hóa số học cung cấp một mã hóa văn bản tối ưu về cơ bản từ mô hình đó. Theo một nghĩa nào đó, điều này "giải quyết" vấn đề nén cho mô hình đó. (Vì vậy, trong thực tế, vấn đề chính là mô hình không được biết đến và một số mô hình tốt hơn các mô hình khác về mô hình văn bản của con người.) Nếu nó không được phép mắc lỗi trong cuộc thi này, thì bằng ngôn ngữ của phần trước , một cách để tạo ra giải pháp cho thách thức này là sử dụng bộ mã hóa số học để tạo tệp "gợi ý" từ mô hình ngôn ngữ và sau đó sử dụng bộ giải mã số học làm "trình thông dịch".

Trong mã hóa cơ bản tối ưu này, cuối cùng chúng ta chi bit -log_2 (p) cho một ký tự có xác suất p và tốc độ bit tổng thể của mã hóa là entropy của Shannon . Điều này có nghĩa là một ký tự có xác suất gần 1/2 mất khoảng một bit để mã hóa, trong khi một ký tự có xác suất 1/1000 mất khoảng 10 bit (vì 2 ^ 10 là khoảng 1000).

Nhưng số liệu chấm điểm cho thử thách này được lựa chọn tốt để tránh nén là chiến lược tối ưu. Chúng ta sẽ phải tìm ra một số cách để tạo ra một số lỗi như một sự đánh đổi để có được một tệp gợi ý ngắn hơn. Ví dụ: một chiến lược mà người ta có thể thử là một chiến lược phân nhánh đơn giản: chúng ta thường cố gắng sử dụng mã hóa số học khi có thể, nhưng nếu phân phối xác suất từ ​​mô hình là "xấu" theo cách nào đó chúng ta chỉ đoán được ký tự có khả năng nhất và không ' Hãy thử mã hóa nó.

Tại sao mắc lỗi?

Hãy phân tích ví dụ từ trước để thúc đẩy lý do tại sao chúng ta có thể muốn mắc lỗi "cố ý". Nếu chúng ta sử dụng mã hóa số học để mã hóa ký tự chính xác, chúng ta sẽ dành khoảng một bit trong trường hợp Ehoặc T, nhưng khoảng mười bit trong trường hợp của một A.

Nhìn chung, đây là một mã hóa khá tốt, chi tiêu hơn một chút cho mỗi ký tự mặc dù có ba khả năng; về cơ bản, điều Anày khá khó xảy ra và cuối cùng chúng ta không chi tiêu mười bit tương ứng của nó. Tuy nhiên, liệu có tốt không nếu chúng ta chỉ có thể gây ra lỗi thay vì trong trường hợp A? Xét cho cùng, số liệu cho vấn đề coi 1 byte = 8 bit có độ dài tương đương với 2 lỗi; do đó, có vẻ như người ta nên thích một lỗi thay vì chi nhiều hơn 8/2 = 4 bit cho một ký tự. Chi nhiều hơn một byte để lưu một lỗi chắc chắn nghe có vẻ không tối ưu!

Cơ chế "tua lại"

Phần này mô tả khía cạnh thông minh chính của giải pháp này, đây là một cách để xử lý các dự đoán không chính xác mà không mất chi phí.

Đối với ví dụ đơn giản mà chúng tôi đã phân tích, cơ chế tua lại đặc biệt đơn giản. Trình thông dịch đọc một bit từ tệp gợi ý. Nếu nó là 0, nó đoán E. Nếu nó là 1, nó đoán T. Lần sau khi được gọi, nó sẽ thấy ký tự chính xác là gì. Nếu tệp gợi ý được thiết lập tốt, chúng tôi có thể đảm bảo rằng trong trường hợp Ehoặc T, trình thông dịch đoán chính xác. Nhưng còn cái gì A? Ý tưởng của cơ chế tua lại đơn giản là không mã hóa Agì cả . Chính xác hơn, nếu sau đó người phiên dịch biết rằng ký tự chính xác là một A, nó ẩn dụ " tua lại cuộn băng": nó trả về bit mà nó đã đọc trước đó. Bit nó đọc có ý định mã EhoặcT, nhưng không phải bây giờ; nó sẽ được sử dụng sau Trong ví dụ đơn giản này, về cơ bản, điều này có nghĩa là nó tiếp tục đoán cùng một nhân vật ( Ehoặc T) cho đến khi nó đúng; sau đó nó đọc một bit khác và tiếp tục đi.

Mã hóa cho tệp gợi ý này rất đơn giản: biến tất cả các Es thành 0 bit và Ts thành 1 bit, trong khi bỏ qua Ahoàn toàn s. Bằng cách phân tích ở cuối phần trước, sơ đồ này tạo ra một số lỗi nhưng làm giảm điểm tổng thể bằng cách không mã hóa bất kỳ của As. Là một hiệu ứng nhỏ hơn, nó thực sự cũng tiết kiệm độ dài của tệp gợi ý, vì cuối cùng chúng tôi sử dụng chính xác một bit cho mỗi ETthay vì hơi nhiều hơn một chút.

Một định lý nhỏ

Làm thế nào để chúng ta quyết định khi nào gây ra lỗi? Giả sử mô hình của chúng tôi cung cấp cho chúng tôi phân phối xác suất P cho ký tự tiếp theo. Chúng tôi sẽ tách các ký tự có thể thành hai lớp: được mã hóa không được mã hóa . Nếu ký tự chính xác không được mã hóa, thì cuối cùng chúng ta sẽ sử dụng cơ chế "tua lại" để chấp nhận lỗi mà không mất phí. Nếu ký tự đúng được mã hóa, thì chúng ta sẽ sử dụng một số phân phối Q khác để mã hóa nó bằng mã hóa số học.

Nhưng chúng ta nên chọn phân phối nào? Không quá khó để thấy rằng tất cả các ký tự được mã hóa đều có xác suất (tính bằng P) cao hơn các ký tự không được mã hóa. Ngoài ra, phân phối Q chỉ nên bao gồm các ký tự được mã hóa; Rốt cuộc, chúng tôi không mã hóa những cái khác, vì vậy chúng tôi không nên "chi tiêu" entropy cho chúng. Sẽ khó hơn một chút khi thấy rằng phân phối xác suất Q phải tỷ lệ thuận với P trên các ký tự được mã hóa. Đặt các quan sát này lại với nhau có nghĩa là chúng ta nên mã hóa các ký tự có khả năng nhất nhưng có thể không phải là các ký tự ít có khả năng hơn và Q chỉ đơn giản là P được định cỡ lại trên các ký tự được mã hóa.

Ngoài ra, nó còn chỉ ra rằng có một định lý hay về việc "cắt" nên chọn các ký tự mã hóa: bạn nên mã hóa một ký tự miễn là ít nhất là 1 / 5.393 như các ký tự được mã hóa khác kết hợp. Điều này "giải thích" sự xuất hiện của hằng số dường như ngẫu nhiên ở 5.393gần cuối chương trình ở trên. Số 1 / 5.393 0.18542 là giải pháp cho phương trình -p log (16) - p log p + (1 + p) log (1 + p) = 0 .

Có lẽ đó là một ý tưởng hợp lý để viết ra thủ tục này trong mã. Đoạn mã này có trong 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;
}

Để tất cả chúng cùng nhau

Phần trước không may là một chút kỹ thuật, nhưng nếu chúng ta đặt tất cả các phần khác lại với nhau, cấu trúc như sau. Bất cứ khi nào chương trình được yêu cầu dự đoán nhân vật tiếp theo sau một ký tự chính xác:

  1. Thêm ký tự chính xác vào tiền tố chính xác đã biết của Moby Dick.
  2. Cập nhật mô hình (Markov) của văn bản.
  3. Các nước sốt bí mật : Nếu đoán trước đây là không chính xác, tua lại trạng thái của bộ giải mã số học để trạng thái của nó trước khi đoán trước!
  4. Yêu cầu mô hình Markov dự đoán phân phối xác suất P cho ký tự tiếp theo.
  5. Chuyển đổi P thành Q bằng chương trình con từ phần trước.
  6. Yêu cầu bộ giải mã số học giải mã một ký tự từ phần còn lại của tệp gợi ý, theo phân phối Q.
  7. Đoán nhân vật kết quả.

Mã hóa của tập tin gợi ý hoạt động tương tự. Trong trường hợp đó, chương trình biết nhân vật tiếp theo chính xác là gì. Nếu đó là một ký tự nên được mã hóa, thì dĩ nhiên người ta nên sử dụng bộ mã hóa số học trên nó; nhưng nếu nó không phải là ký tự không được mã hóa, thì nó sẽ không cập nhật trạng thái của bộ mã hóa số học.

Nếu bạn hiểu nền tảng lý thuyết thông tin như phân phối xác suất, entropy, nén và mã hóa số học nhưng đã cố gắng và không hiểu bài này (ngoại trừ lý do tại sao định lý là đúng), hãy cho chúng tôi biết và chúng tôi có thể cố gắng làm sáng tỏ mọi thứ. Cảm ơn vì đã đọc!


8
Wow, câu trả lời ấn tượng. Tôi giả sử có mã bổ sung cần thiết để tạo Btập tin? Nếu vậy, xin vui lòng bạn có thể bao gồm điều đó trong câu trả lời của bạn?
Nathaniel

8
Xuất sắc! Câu trả lời đầu tiên (và cho đến nay là duy nhất) để phá vỡ rào cản điểm 500k.
ShreevatsaR

5
"đã bắt cá voi shite" omg Tôi đang khóc
Phill

5
Vì không có câu trả lời mới nào được đăng trong giai đoạn tiền thưởng, tôi trao giải cho câu trả lời của bạn, vì cả cách ghi điểm tốt nhất và cách tiếp cận tinh vi nhất. Nếu bạn có thời gian, tôi thực sự sẽ đánh giá cao một lời giải thích sâu hơn về cách câu trả lời này hoạt động, tức là chính xác thuật toán là gì?
Nathaniel

2
@Nathaniel: Tôi đã thêm một lời giải thích cho bài viết này. Hãy cho tôi biết nếu bạn nghĩ rằng nó đủ chi tiết để tự tái tạo giải pháp.
A. Rex

77

Trăn 3, 2 · 267 + 510193 = 510727

Dự đoán

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]

Điều này sử dụng kết hợp Bayesian có trọng số của các mô hình 0, độ, 16 Markov, với các trọng số [1, 6, 12, 30, 65, 99, 87, 117, 118, 89, 95, 118, 96, 184, 126, 219, 126].

Kết quả không nhạy cảm lắm với việc lựa chọn các trọng số này, nhưng tôi đã tối ưu hóa chúng bởi vì tôi có thể, sử dụng cùng một thuật toán leo đồi chấp nhận muộn mà tôi đã sử dụng trong câu trả lời của mình với một nhóm đa số Thượng viện , trong đó mỗi đột biến ứng cử viên là chỉ tăng 1 đến một trọng lượng.

Mã kiểm tra

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
Công cụ phù hợp cho công việc. Điểm số tuyệt vời. Đẹp một.
bất cứ lúc nào

1
Có thể làm rõ: b"\0\3\6\r\34'&-20'\22!P\n[\26"là đại diện ascii của các trọng số, trong đó các giá trị nhỏ không thể in được thoát trong bát phân.
Cœur

Tôi đã cập nhật câu hỏi với một phiên bản của tệp mà văn bản không được bọc - bạn có thể thử chạy lại mã của mình trên đó (nó có thể làm tốt hơn một chút)
Nathaniel

3
Cảm ơn lời giải thích đó - nếu bạn có thể chỉnh sửa tóm tắt thành câu hỏi thì thật tuyệt. (Kinh nghiệm với thử thách trước đây của tôi Paint Starry Night là các quy trình tối ưu hóa này là phần thú vị nhất của câu trả lời, vì vậy sẽ tốt hơn nhiều nếu câu trả lời bao gồm mã được sử dụng để làm điều đó và giải thích về nó. Tôi bao gồm một quy tắc trong cả hai những thách thức nói rằng họ nên.)
Nathaniel

1
@Christoph Kết hợp mô hình của tôi thực sự là một ý nghĩa hình học có trọng số. Nhưng tính trung bình của PAQ trong lĩnh vực logistic hơi khác nhau, tôi sẽ phải xem liệu điều đó có tốt hơn không.
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 = 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

Hãy thử trực tuyến!

Một hàm sử dụng các biến toàn cục. Học khi nó đi, xây dựng một mô hình ở cấp độ từ: dựa trên những gì nó đã thấy cho đến nay trong từ này , nhân vật tiếp theo phổ biến nhất là gì? Khi có nhiều đầu vào, nó học các từ phổ biến từ văn bản khá tốt, và nó cũng học được ký tự phổ biến nhất để bắt đầu từ tiếp theo .

Ví dụ:

  • Nếu cái mà nó nhìn thấy cho đến nay là 'Captai' thì nó dự đoán là "n"
  • Nếu là "Thuyền trưởng", nó dự đoán một khoảng trắng
  • Nếu đó là bắt đầu của một từ và từ cuối cùng là "Thuyền trưởng" thì nó dự đoán là 'A'
  • Nếu từ cho đến nay là 'A', nó dự đoán 'h' (và sau đó 'a' và 'b'; tương tự với 'C').

Nó không làm rất tốt khi bắt đầu, nhưng cuối cùng, có những phần lớn các từ thực tế xuất hiện. Tùy chọn dự phòng là một khoảng trắng và sau một khoảng trắng, nó là "a", trừ khi chữ cái trước là một trong số "nedtfo", một chữ số hoặc dấu gạch nối hoặc dấu nháy đơn. Nó cũng tích cực dự đoán ngắt dòng sau 71 ký tự hoặc nếu khoảng trống được dự kiến ​​sau 66. Cả hai chỉ được điều chỉnh theo dữ liệu ("t" là phổ biến hơn nhiều sau một khoảng trắng, nhưng thường được dự đoán nhiều hơn, vì vậy " a "là một phỏng đoán tốt hơn bên ngoài sáu trường hợp đặc biệt).

Tìm hiểu những gì các cặp từ đã đi cùng nhau và sắp xếp trước ánh xạ hóa ra không có giá trị.


Nó kết thúc bằng văn bản như thế này:

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!

tương ứng với phần này của đầu vào:

tất cả xung quanh anh ta.

"Tôi đã nhìn thấy anh ta gần như ngay lập tức, thưa ngài, rằng Đại úy Ahab đã làm, và tôi đã khóc", ông Tashtego nói.

"Không giống nhau ngay lập tức -có cô ấy thổi!

Bạn có thể thấy nơi các danh từ riêng biệt xuất hiện khá tốt, nhưng phần cuối của các từ cũng chủ yếu là đúng. Khi nhìn thấy "nhân đôi", nó mong đợi "sự nghi ngờ", nhưng một khi chữ "l" xuất hiện, nó sẽ bị "nghi ngờ".

Nếu bạn chạy nó lần thứ hai với cùng một mô hình, nó vừa được xây dựng, nó ngay lập tức nhận được 92k chính xác khác (51,7% -> 59,3%), nhưng nó luôn chỉ dưới 60% từ lần lặp thứ hai trở đi.


Mã đo nằm trong liên kết TIO hoặc đây là phiên bản tốt hơn một chút:

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 có đầu ra đoán ở cuối.


3
Đây là một cách tiếp cận tuyệt vời!
Skyler

2
quá nhiều <s> </ s>;)
FantaC

1
+1 vì cách tiếp cận này làm tôi nhớ đến thuật toán nén LZW.
Marcos

25

C ++, điểm: 2 * 132 + 865821 = 866085

Cảm ơn @Quentin vì đã tiết kiệm được 217 byte!

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

Một giải pháp rất đơn giản, được đưa ra một ký tự, chỉ xuất ra ký tự thường xuyên xuất hiện sau ký tự đầu vào.

Xác nhận điểm số với:

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

Chỉnh sửa: Sử dụng whale2.txtcho điểm số tốt hơn.


5
Bạn có thể dịch mảng này thành một chuỗi bằng chữ và đặt nó trực tiếp vào vị trí Lđể lưu một loạt các ký tự :)
Quentin

@Quentin Cảm ơn! Bây giờ tôi đang tự hỏi tại sao tôi không nghĩ về điều đó ngay từ đầu ...
Steadybox

20

Con trăn, 2 * 516 + 521122 = 522154

Thuật toán:

Một lần gửi trăn khác, thuật toán này tính toán chữ cái tiếp theo rất có thể đang xem xét các chuỗi có độ dài 1, ..., l. Tổng xác suất được sử dụng và có một vài thủ thuật để có kết quả tốt hơn.

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

Các kết quả:

Chủ yếu là vô nghĩa, mặc dù bạn có thể thấy nó xuất hiện trên cụm từ thỉnh thoảng, chẳng hạn như "Cha 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

Mã kiểm tra:

Khá đơn giản, xuất ra một số ví dụ của văn bản tại các điểm khác nhau. Sử dụng whale2.txt, vì điều này tránh một số logic bổ sung để tính toán các dòng mới.

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
Chào mừng đến với trang web! Đây là một đệ trình đầu tiên tuyệt vời. :)
DJMcMayhem

@DJMcMayhem, Cảm ơn đã chào đón. Bây giờ tôi rất thích xem, đây là cuộc thi đầu tiên thu hút sự chú ý của tôi cho một mục.
dùng2699

19

C (gcc) , 679787 652892

84 76 byte, 679619 652740 đoán không chính xác

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

Hãy thử trực tuyến!

Cập nhật: ~ 27000 điểm với tệp cập nhật, 16 điểm (8 byte) với chức năng chơi golf tốt hơn.

Giải trình

Cách thức hoạt động này là khi mã chạy qua văn bản, nó ghi nhớ ký tự cuối cùng chấm dứt bất kỳ chuỗi 4 ký tự đã cho nào và trả về giá trị đó. Tương tự như cách tiếp cận của Arnauld ở trên, nhưng dựa vào khả năng vốn có của hai chuỗi 4 ký tự đã cho kết thúc theo cùng một cách.

Bỏ chơi gôn:

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

... Liên kết TIO là vô dụng. Vậy hàm trả về giá trị của phép gán cuối cùng?
dùng202729

Hãy để tôi chỉnh sửa câu trả lời bằng một lời giải thích, sau đó :)

1
@Rogem Tôi đã thêm một phiên bản không chơi gôn (tôi đã làm vì tôi cũng không thể làm theo) - hy vọng điều này không làm phiền bạn nhưng vui lòng quay lại nếu muốn.
Adam Davis

@AdamDavis trên hầu hết các triển khai của C, tất cả các biến toàn cục bắt đầu từ 0. Đó là hành vi không xác định, vì vậy nó chỉ được sử dụng trong môn đánh gôn.
NieDzejkob

1
@NieDzejkob Ah, bạn nói đúng, cảm ơn! "ANSI-C yêu cầu tất cả các biến tĩnh / toàn cầu chưa được khởi tạo phải được khởi tạo bằng 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

theo sau là whale2.txt được nén bzip2 với byte đầu tiên bị thiếu

Bỏ qua đầu vào của nó; đưa ra câu trả lời đúng. Điều này cung cấp một đường cơ sở trên một đầu; daniero cung cấp một đường cơ sở ở đầu kia.

Tập lệnh xây dựng:

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

Khai thác kiểm tra I / O (tcc; cắt dòng đầu tiên cho gcc). Bất kỳ ai cũng có thể sử dụng khai thác thử nghiệm này trên một nền tảng phù hợp để gửi một chương trình hoàn chỉnh mong đợi đọc / ghi I / O. Nó sử dụng I / O theo thời gian để tránh gian lận. Chương trình con phải tuôn ra đầu ra sau mỗi byte để tránh bị chặn.

#!/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
Tôi nghĩ những gì anh ấy hỏi là: làm thế nào điều này không vi phạm 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.điều khoản?

8
@Rogem Dữ liệu nén được đặt sau những gì được hiển thị ở đây và mã truy cập chính nó.
dùng202729

4
Câu hỏi cho biết: "Nội dung gửi của bạn sẽ là một chương trình hoặc chức năng (v.v.) sẽ được gọi hoặc gọi nhiều lần. Khi được gọi, nthnó sẽ được đưa ra ký tự thứ n của whale.txthoặc whale2.txtvà nó phải đưa ra dự đoán của nó cho (n+1)thtính cách." - Yêu cầu này được thực hiện như thế nào? Mã này hiển thị toàn bộ văn bản whale.txtmỗi khi nó được thực thi.
axiac

1
@axiac "mọi thứ đều ổn, miễn là chương trình của bạn luôn cung cấp một byte đầu ra trước khi nhận byte đầu vào tiếp theo."
dùng202729

5
@axiac đưa ra khai thác thử nghiệm, tôi rất vui khi coi việc gửi chương trình một byte từ STDIN là "gọi hoặc gọi" nó. Điều nhập khẩu là chương trình trả về một byte đầu ra sau mỗi byte đầu vào, điều này thực sự làm, khi chạy qua khai thác thử nghiệm. Như câu hỏi nói, "mọi thứ đều ổn, miễn là chương trình của bạn luôn cung cấp một byte đầu ra trước khi nhận byte đầu vào tiếp theo."
Nathaniel

13

Trăn 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])

Hãy thử trực tuyến!


... Câu ///trả lời in một khoảng trắng nhận được 10 upvote, trong khi mã của tôi chỉ có thể nhận được 3 ...

Giải trình:

Đối với mỗi nhân vật, chương trình:

  • tăng frequency[prev][char]
  • Tìm nhân vật xuất hiện nhiều lần nhất trong frequency[char]
  • và đầu ra nó.

  • Mã Ungolfed trong liên kết TIO, nhận xét.
  • Mã này là 131 byte.
  • Mã chạy trên máy của tôi báo cáo:
879504 / 1215235
Time: 62.01348257784468

trong đó có tổng số điểm

2*131 + 879504 = 879766

Vì không có cách nào để tải một tệp lớn lên TIO (ngoại trừ hỏi Dennis), ví dụ chạy trong liên kết TIO chỉ chạy chương trình cho một phần nhỏ của văn bản.

So với câu trả lời cũ hơn, câu trả lời này có 362 ký tự không chính xác hơn, nhưng mã ngắn hơn 255 byte. Số nhân làm cho bài nộp của tôi có điểm thấp hơn.


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

Cách tiếp cận này sử dụng bảng tra cứu để tìm hiểu ký tự phổ biến nhất theo chuỗi đã cho. Các phím của bảng tra cứu có tối đa 4 ký tự, do đó, chức năng trước tiên cập nhật bảng tra cứu với ký tự hiện tại và sau đó chỉ kiểm tra ký tự nào có khả năng xảy ra nhất sau 4 ký tự trước bao gồm cả ký tự hiện tại . Nếu 4 ký tự đó không được tìm thấy trong bảng tra cứu, nó sẽ in một khoảng trắng.

Phiên bản này sử dụng whale2.txttệp, vì nó cải thiện đáng kể số lần đoán thành công.

Sau đây là mã được sử dụng để kiểm tra lớp:

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

Mã này chỉ chạy trong 2 giây. Chỉ dành cho bản ghi, đây là những gì tôi nhận được khi sửa đổi kích thước các phím của bảng tra cứu (bao gồm kết quả của lần chạy thứ hai mà không đặt lại mô hình):

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

Sẽ rất thú vị khi biết tại sao kích thước khóa gồm 4 ký tự là lựa chọn tốt nhất trong thuật toán này.

So sánh văn bản

Nguyên:

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

Tái tạo:

"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 

Đoán:

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

Thay đổi nhật ký

  • 569279 - thay đổi thành whale2.txtvà do đó loại bỏ tối ưu hóa.
  • 577366 - được tối ưu hóa với mã đã cố đoán khi nào sẽ trả về nguồn cấp dữ liệu.
  • 590354 - phiên bản gốc.

4
Cảm ơn bạn đã hiển thị phương sai khi bạn thay đổi kích thước khóa và ngưỡng cột!
Jeremy Weirich

Tôi đã cập nhật câu hỏi với một phiên bản của tệp mà văn bản không được bọc - bạn có thể có thể lưu một số điểm bằng cách sử dụng
Nathaniel

@Nathaniel nó thực sự. Tôi đã cập nhật câu trả lời.
Charlie

Bạn có thể lưu một số byte bằng cách sử dụng var thay vì khai báo các loại.
Ed T

1
Khi kích thước khóa trở nên lớn hơn, số lần truy cập bị bỏ lỡ sẽ giảm xuống và do đó sẽ có nhiều khoảng trống hơn khi một phím ngắn hơn có thể đoán đúng ký tự. Khi kích thước khóa trở nên nhỏ hơn, các dự đoán riêng lẻ sẽ kém chính xác hơn cho các phân đoạn khớp. Tôi nghi ngờ đây là lý do tại sao một chiều dài bốn là tối ưu. Nếu bạn duy trì các khóa có nhiều độ dài và sử dụng các kết quả ngắn hơn khi không có khóa dài hơn, thì tôi cho rằng tỷ lệ trúng (và do đó điểm số) sẽ được cải thiện đáng kể ở độ dài khóa dài hơn.
Jeffrey L Whitledge

11

Java 7, 1995 ký tự, (1995 * 2 + 525158) 529148

Java hút cho kích thước chương trình nhỏ. Dù sao, tôi đã thử một số cách tiếp cận cực kỳ phức tạp và phức tạp tạo ra kết quả tào lao đáng ngạc nhiên. Sau đó tôi đã quay lại và chỉ thực hiện một cách tiếp cận đơn giản, dẫn đến quy mô chương trình nhỏ hơn và kết quả tốt hơn.

Cách tiếp cận này thực sự cực kỳ đơn giản. Nó mù quáng đưa các ký tự x trước đó (ngoài tất cả các chuỗi con của các ký tự đó) vào một hàm băm, ánh xạ tới ký tự hiện tại. Sau đó, nó theo dõi các mẫu dự đoán chính xác nhất về nhân vật hiện tại. Nếu các mẫu có trước một số ký tự được bắt gặp nhiều lần, chúng sẽ thành công trong việc dự đoán ký tự. Nó ưu tiên cho các chuỗi dài hơn và nó ưu tiên cho bất kỳ ký tự nào thường theo sau một chuỗi nhất định. Thuật toán này không biết gì về loại tài liệu hoặc ngôn ngữ tiếng Anh.

Tôi quyết định sử dụng 9 ký tự và cố gắng khớp toàn bộ các từ trong 9 ký tự trước đó khi có thể. Khi bạn không thử thực hiện so khớp từ trong chuỗi, độ dài tối ưu là 6 ký tự, tạo ra hàng nghìn lỗi sai.

Một quan sát thú vị là việc sử dụng 20 ký tự dẫn đến dự đoán xấu lần đầu tiên nhưng độ chính xác 99,9% cho các lần truyền tiếp theo. Thuật toán về cơ bản có thể ghi nhớ cuốn sách trong các đoạn 20 byte chồng chéo, và điều này đủ khác biệt để cho phép nó nhớ lại toàn bộ cuốn sách một nhân vật tại một thời điểm.

  • (1950 * 2 + 532919) 536819
  • (2406 * 2 + 526233) 531045 kiểm tra dấu câu để đoán chính xác hơn
  • (1995 * 2 + 525158) 529148 điều chỉnh nhiều hơn, bỏ đi một số thông báo

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

Đó là một điểm khá tốt cho một ngôn ngữ dài dòng như vậy.
DJMcMayhem

1
Tôi cho rằng nó đáng để thử vì kích thước của tệp mang lại nhiều khoảng trống để cải thiện so với kích thước chương trình.
Jim W

3
Điều này không thể biên dịch được trong Java 7 (hoặc bất kỳ phiên bản Java nào, với giá trị của nó). Bạn vui lòng sửa mã của bạn? Khi đã xong, tôi sẽ vui lòng đánh gôn để điểm số của bạn được cải thiện.
Olivier Grégoire

Chưa được kiểm tra, nhưng đây phải là mã chính xác của bạn hơi bị đánh gôn: 950 byte . Mã hiện tại của bạn có khá nhiều lỗi, vì vậy tôi không chắc mình có điền đúng mọi thứ không. Một lần nữa, chưa được kiểm tra, vì vậy chỉ cần so sánh các phiên bản để xem những gì tôi đã thay đổi / đổi tên và xem mọi thứ có còn hoạt động giống như mã gốc của bạn không. Chắc chắn có thể được chơi golf nhiều hơn, mặc dù.
Kevin Cruijssen

Crap, tôi đã làm điều này trong khi buồn chán ở công việc cũ của tôi và không mang theo mã với tôi. Tôi sẽ phải xem nó để biết lỗi đánh máy ở đâu.
Jim W

10

Trăn 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

Tôi đã cố gắng làm điều này một cách độc lập, nhưng cuối cùng lại có một phiên bản kém hơn trong câu trả lời của Michael Homer. Tôi hy vọng điều đó không khiến câu trả lời của tôi hoàn toàn lỗi thời.

Điều này xây dựng theo thời gian một từ điển các từ (được định nghĩa thô sơ là các chuỗi kết thúc bởi hoặc\n , phân biệt chữ hoa chữ thường và bao gồm cả dấu câu). Sau đó, nó tìm kiếm từ điển này cho các từ bắt đầu bằng những gì nó biết từ trước đến nay, sắp xếp danh sách kết quả theo tần suất xuất hiện (từ từ) và đoán rằng ký tự tiếp theo là ký tự tiếp theo trong từ phù hợp phổ biến nhất. Nếu chúng ta đã có từ phù hợp phổ biến nhất hoặc không còn từ phù hợp tồn tại, nó sẽ trả về .

Nó cũng xây dựng một từ điển không hiệu quả của các cặp từ. Khi nhấn vào một ranh giới từ, nó đoán rằng ký tự tiếp theo là chữ cái đầu tiên của từ thứ hai trong cặp từ phù hợp phổ biến nhất hoặc tnếu không có từ nào khớp. Nó không phải là rất thông minh, mặc dù. Tiếp theoMoby , chương trình đoán chính xác rằng nhân vật tiếp theo làD , nhưng sau đó nó quên tất cả về bối cảnh và cuối cùng thường gọi con cá voi là "Vịt Moby" (vì từ "Dutch" dường như thường xuyên hơn trong nửa đầu của văn bản ). Có thể dễ dàng khắc phục điều này bằng cách ưu tiên các cặp từ hơn các từ riêng lẻ, nhưng tôi hy vọng mức tăng sẽ không đáng kể (vì nó thường chính xác từ ký tự thứ ba trở đi và các cặp từ không hữu ích ở vị trí đầu tiên).

Tôi có thể điều chỉnh điều này để phù hợp hơn với văn bản được cung cấp, nhưng tôi không nghĩ việc điều chỉnh thuật toán theo cách thủ công dựa trên kiến ​​thức trước đó về đầu vào thực sự theo tinh thần của trò chơi, ngoài việc chọn t làm nhân vật dự phòng sau một khoảng trắng ( và tôi có lẽ không nên làm điều đó), tôi đã tránh điều đó. Tôi đã bỏ qua độ dài dòng đã biết của tệp đầu vào và thay vào đó đã chèn\n đó, cứ sau 13 khoảng trống, điều này gần như chắc chắn là một kết quả rất kém, mục đích chính là giữ cho độ dài của dòng hợp lý thay vì khớp với đầu vào.

Mã không chính xác nhanh (~ 2 giờ trên máy của tôi), nhưng tổng thể có khoảng một nửa số ký tự đúng (49%). Tôi hy vọng điểm số sẽ tốt hơn một chút nếu chạy tiếp whale2.txt, nhưng tôi đã không làm được điều đó.

Bắt đầu của đầu ra trông như thế này:

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

nhưng cuối cùng, nó trông hơi giống ... một cái gì đó. Đoạn văn yêu thích của tôi từ gần cuối cuốn sách,

và vì không ai có thể là của tôi, hãy để tôi kéo thành từng mảnh, trong khi vẫn đuổi theo ngươi, mặc dù bị trói vào ngươi, cá voi chết tiệt! THUS, tôi từ bỏ giáo! "

đi ra như

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 "

Điều đó sẽ khiến The Wrath of Khan trở nên khó hiểu hơn rất nhiều. Và "cô đơn" → "trêu chọc" là một sự thay thế đặc biệt thỏa mãn.

Chỉnh sửa: Đã lưu một byte bằng cách xóa một không gian bên ngoài

Chấm điểm

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

Điều này chạy chương trình cho văn bản của Moby Dick và đưa ra văn bản xuất chuẩn "dự đoán" và lạm dụng stderr để ghi điểm. Tôi khuyên bạn nên chuyển hướng đầu ra thành một tập tin.


2
Chào mừng đến với PPCG!
Martin Ender

1
Sẽ không lambda i:i[1]rẻ hơn so với giao dịch operator?
Draconis

@Draconis Hầu như chắc chắn.
georgewatson

9

C ++, 2 · 62829 + 318786 = 444444

Để chạy chương trình này, bạn cần tệp này ở đây , phải được đặt tên C.

Chương trình sử dụng kết hợp các mô hình Markov giống như trong câu trả lời trước đó của chúng tôi . Như trước đây, sự kết hợp này về cơ bản là mô hình từ câu trả lời này của user2699 , nhưng với một vài sửa đổi nhỏ.

Xem làm thế nào câu trả lời này sử dụng mô hình chính xác như trước đây, cải tiến là một cơ chế lý thuyết thông tin tốt hơn hơn so với "tua lại" được mô tả trước đó. Điều này cho phép nó tạo ra ít lỗi hơn trong khi cũng có độ dài kết hợp nhỏ hơn. Bản thân chương trình không được đánh gôn nhiều vì nó không phải là đóng góp chính cho điểm số.

Chương trình dài 2167 byte (bao gồm tất cả các tab để thụt lề và nhiều ký tự không cần thiết khác, nhưng trước mã kiểm tra) và tệp nhị phân Cdài 60661 byte, do đó, theo quy tắc cho nhiều điểm , chúng tôi ghi điểm Llà 2167 + 60661 + 1 = 62829.

Chương trình mất khoảng 8 phút để chạy trên một m5.4xlargephiên bản trên Amazon EC2 và sử dụng ít hơn 16 GB bộ nhớ. (Việc sử dụng bộ nhớ quá mức này là không cần thiết - chúng tôi cũng không tối ưu hóa điều đó.)

#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

Trăn 3, 526640

274 byte, 526092 lỗi (sử dụng whale2.txt). Điều này chắc chắn có khả năng cải thiện hơn nữa, nhưng nó đã đạt đến giai đoạn "đủ tốt để đăng".

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

Ý tưởng là lưu trữ tần số của tất cả các lần chạy 2, 3, 4, ..., 10 ký tự. Đối với mỗi độ dài L này, chúng tôi kiểm tra xem các ký tự L-1 gần đây nhất có khớp với mẫu được lưu trữ hay không; nếu vậy, chúng tôi đoán g L là nhân vật tiếp theo thường xuyên nhất theo mô hình đó. Chúng tôi thu thập tới chín lần đoán theo cách này. Để quyết định sử dụng dự đoán nào, chúng tôi cân tần số của từng mẫu theo độ dài của nó đến công suất thứ 8. Việc đoán với tổng tần số lớn nhất được chọn. Nếu không có mẫu nào khớp, chúng tôi đoán không gian.

(Độ dài mẫu tối đa và số mũ theo trọng số đã được chọn bởi thử và sai để đưa ra dự đoán không chính xác nhất.)

Đây là phiên bản tiến hành chưa hoàn thành của tôi:

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

Và khai thác thử nghiệm:

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

Đây là một số đầu ra mẫu từ gần đầu văn bản. Đã được chúng tôi bắt đầu thấy khả năng hoàn thành từ thông dụng sau khi nhìn thấy chữ cái đầu tiên của họ ( in, to, and, by; cũng vậy, rõ ràng, 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 

Gần cuối, vẫn còn rất nhiều sai lầm, nhưng cũng có rất nhiều trình tự rất tốt ( shmage seashawksví dụ).

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

Thật thú vị khi xem xét một số lỗi và đoán từ "thuật toán" dự kiến. Ví dụ, sau khi sail, chương trình cả hai lần dự đoán o--cho sailor, tôi đoán. Hoặc một lần nữa, sau khi , anó mong đợi - có nthể là do sự xuất hiện phổ biến của , and.


Thay đổi:

  • 274 * 2 + 526092 = 526640 Đánh gôn thuật toán, với chi phí của một vài lỗi thêm
  • 306 * 2 + 526089 = 526701 Phiên bản gốc

6

Python 2, điểm: 2 * (407 + 56574) + 562262 = 676224

Tìm kiếm các từ khớp với các ký tự trước từ danh sách  tất cả  các từ được sử dụng trong văn bản, được sắp xếp theo số lần xuất hiện của chúng.

Mã số:

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"

Dữ liệu: https://www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0

Bộ kiểm tra:

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

Chỉnh sửa: Sử dụng whale2.txtcho điểm số tốt hơn.


5

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

Một trình tiền tố tần số khác. Chạy trên whale2.txtvà nhận được điểm tương tự (hơi tệ hơn) so với những người khác.

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

Điều này tham lam tìm chuỗi dài nhất bắt đầu bằng hậu tố của lịch sử và nếu có nhiều ứng cử viên, hãy kết hợp với chuỗi ngắn hơn.

Ví dụ: Nếu 7 ký tự cuối cùng là abcdefgh, và chuỗi abcdefghiabcdefghjxuất hiện với tần số lớn nhất trong tất cả các chuỗi có dạng abcdefgh*, sản lượng sẽ là một trong hai ihoặc j, tiebreak với hậu tố ngắn hơn ( bcdefgh, cdefgh, ...).

Không rõ lý do, mọi thứ hơn 7 và máy tính của tôi không có đủ RAM để chạy. Ngay cả với 7, tôi cần đóng tất cả các trình duyệt web để chạy nó.


Mã kiểm tra:

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

Ung dung:

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

Ví dụ đầu ra:

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

Điều này là gần cuối của văn bản. Hầu hết các từ dài được dự đoán khá chính xác ( 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

Chữ hoa có vẻ không tốt.


Trie dường như tiêu tốn nhiều bộ nhớ hơn tôi mong đợi ...
user202729

... Và cũng khó thực hiện hơn.
dùng202729

4

Con trăn 2, 756837

Sử dụng một cái gì đó có thể là chuỗi Markov?

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

1
Giải thích nhanh: Các zlib.decompress('...')đánh giá {'G?':' ', 'G;':' ','G"':' ',.......}alà một từ điển ánh xạ từ 2 ký tự đến 1 ký tự. Về cơ bản biến thể 2 ký tự của câu trả lời của Steadybox .
dùng202729

1
Như tôi có thể thấy, nghĩa đen là 17780 byte. Bạn có thể giảm nó xuống còn 11619 ký tự bằng cách xóa các khoảng trắng trong nội dung được giải nén, giúp tiết kiệm 12322 byte. (nếu tôi đếm đúng) Ngoài ra ... chuyển đổi mã thoát hex thành các ký tự thô thực tế có thể tiết kiệm nhiều byte hơn.
dùng202729

Làm thế nào để tôi đăng một cái gì đó ở đây nếu nó là byte thô?
Skyler

1
xxd, hexdump, uuencode, Hoặc tương tự
Peter Taylor

@ user202729 Chỉ cần lưu ý rằng mã Python không thể chứa các byte NUL thô thực tế.
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)

Thí dụ:

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

Sử dụng ba tệp phụ trợ được tính toán trước:

  • seqwords chứa 236 từ phổ biến nhất.
  • wordsseq chứa một chuỗi được nén LZMA của những từ này và cho tất cả các từ không nằm trong số 236 phổ biến nhất, độ dài.
  • dicttrieschứa, cho mỗi độ dài từ, một cây quyết định chứa tất cả các từ còn lại. Từ những cố gắng này, các mục được chọn khi chúng tôi đi.

Bằng cách này, chúng tôi đạt được tỷ lệ lỗi thấp hơn đáng kể so với tất cả các chương trình tổn thất khác; Thật không may, wordsseqtập tin vẫn còn quá lớn để cạnh tranh.

Đây là phiên bản hoàn chỉnh tạo các tệp và thực hiện phân tích:

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;

Vì nó là giải pháp cho WIP và do đó không được cung cấp. Cũng xem xét rằng kích thước mã thực tế hầu như không có bất kỳ tác động nào đến điểm số tôi nghĩ rằng tôi đã đăng câu trả lời của mình trước khi bắt đầu tối ưu hóa vi mô.
(Mã đầy đủ có sẵn ở đây: https://github.com/BrainStone/MobyDickRNG - Bao gồm toàn bộ chương trình và tìm kiếm hạt giống)

Giải pháp này dựa trên RNG. Đầu tiên tôi phân tích văn bản. Tôi tạo một bản đồ đếm số lần xuất hiện của hai nhân vật liên tiếp. Sau đó, tôi tạo một bản đồ phân phối. Tất cả điều này được thực hiện tĩnh vì vậy phải tuân theo các quy tắc.

Sau đó, trong khi cố gắng in văn bản, tôi thực hiện tra cứu và lấy một ký tự ngẫu nhiên của những cái có thể. Mặc dù điều này thường tạo ra kết quả tồi tệ hơn là chỉ xuất ra chữ cái phổ biến nhất sau đây, nhưng có khả năng hạt giống thần sẽ tạo ra kết quả tốt hơn. Đó là lý do tại sao hạt giống được mã hóa cứng. Tôi hiện đang tìm kiếm hạt giống tốt nhất. Và tôi sẽ cập nhật câu trả lời này một khi tôi tìm thấy hạt giống tốt hơn. Vì vậy, ở lại đăng!

Nếu bất cứ ai muốn tự tìm kiếm hạt giống hoặc sử dụng các RNG khác nhau, vui lòng rẽ nhánh repo.

Phương pháp được sử dụng để tính điểm: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15

Lưu ý mặc dù tổng số điểm là tồi tệ nhất tại thời điểm này, nó đánh bại số lỗi chỉ xuất ra khoảng trắng. Và cơ hội tốt là điểm số sẽ giảm, bằng cách kiểm tra nhiều hạt giống hơn.

Thay đổi

  • 2018/01/24 : Đăng câu trả lời bẩm sinh .
    Hạt đã kiểm tra: 0-50000. Điểm: 2305 * 2 + 1017754 = 1022364
  • 2018/01/24 : Đã chơi golf tối thiểu. Đã thêm liên kết đến phương pháp tính điểm.
    Hạt đã kiểm tra: 0-80000. Điểm: 1920 * 2 + 1017754 = 1021594 (-770)
  • 2018/02/02 : Hạt giống mới (10087702) (không tìm thấy thời gian để sửa lỗi)
    Hạt giống đã kiểm tra: 0-32000000. Điểm: 1923 * 2 + 1017344 = 1021190 (-404)

Bạn có thể bao gồm một khai thác kiểm tra trong câu trả lời của bạn để đánh giá điểm số?
Nathaniel

@Nathaniel Tôi liên kết mã điểm trực tiếp. Ngoài kho lưu trữ, bạn sẽ xem xét điều này đủ?
BrainStone

Khi xem xét các quy tắc tôi đã nhận thấy rằng tôi vi phạm một số trong số họ. Tự nhiên tôi sẽ cập nhật câu trả lời của mình sau khi tôi khắc phục các sự cố
BrainStone

Sau đó, bạn sẽ mã hóa văn bản vào hạt giống ngẫu nhiên. Xem Seed ngôn ngữ lập trình bí truyền , và bạn có thể muốn thiết kế ngược chương trình MT19937 và đánh bại câu trả lời này (nếu bạn có thể).
dùng202729

Ý tưởng hay, nhưng sẽ không giúp đạt điểm cao. +1 dù sao đi nữa.
dùng202729

3

Ruby, 1164418 (ouch)

Tôi chỉ muốn xem tôi có thể làm tốt như thế nào mà không cần kiểm tra bất kỳ câu trả lời nào khác.
Tôi không chắc liệu điều này có được phép hay không bởi vì nó bao gồm một nghĩa đen mà tôi đã tạo thông qua việc phân tích tệp, nhưng ngay cả khi nó không giống như nó có nguy cơ đánh bại bất cứ ai.

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

Cách tôi tạo x

Đầu tiên, tôi tạo ra a.txtnhư sau:

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

Sau đó, tôi tạo ra a.csv:

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

Sau đó, tôi phân tích nó thành xtập lệnh Ruby sau:

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

Làm thế nào tôi ghi bàn

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

Tôi chắc chắn điều đó được cho phép; Nếu bạn phân tích các tập tin, công việc tốt! Chỉ khi chương trình làm như vậy thì điều này không hợp lệ.
Erik the Outgolfer

@EriktheOutgolfer> _> (âm thầm trượt một "(không cạnh tranh)" vào tiêu đề)
NO_BOOT_DEVICE

Tại sao? Nếu điều này là hợp lệ, nó sẽ cạnh tranh, mặc dù nó có thể không đánh bại nhiều. Nếu nó không hợp lệ (nghĩa là giải pháp của bạn đọc từ tệp và không chỉ chứa một chữ), thì nó sẽ bị xóa.
Erik the Outgolfer

Hừm. Tôi nghĩ bạn có nghĩa là nếu bất kỳ chương trình phân tích các tập tin, và không chỉ là giải pháp.
NO_BOOT_DEVICE

1
Tôi không thể đọc Ruby, nhưng tôi nghĩ nó hợp lệ. Có nghĩa đen trong chương trình là hoàn toàn tốt, nó không có vấn đề gì cả.
Nathaniel

2

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

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

Hãy thử trực tuyến!

Bảng tần số khá đơn giản. Mỗi vị trí trong chuỗi tương ứng với mã ascii của ký tự hiện tại (trừ 10 = 0x0a = '\ n', ký tự thấp nhất trong tệp) và ký tự ở mỗi chỉ mục là ký tự tiếp theo thường xuyên nhất. Giả sử tôi đã tính đúng tần số

Đã kiểm tra mã từ kiểm tra của user202729


Bạn có thể lưu một số byte bằng cách sử dụng def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]?
Neil

0

[Python 3] (644449 * 2 + 0) 1288898 điểm

Độ chính xác hoàn hảo chỉ trong 644449 byte

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

Mã đầy đủ không thể phù hợp với một câu trả lời, vì vậy tôi đã đặt nó ở đây và thay thế chuỗi nhị phân lớn bằng chữ b '###' trong văn bản trả lời.

Mã này được tạo với mã sau đây, trong đó "đã sửa đổi" là tệp được tạo và "chcoateet.txt" là tệp whale2.txt bắt đầu từ ký tự thứ hai.

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

Mã này có thể được thực thi bằng cách thêm đoạn mã sau vào cuối "Sửa đổi". "whale2.txt" phải nằm trong cùng thư mục với "đã sửa đổi" và đầu ra sẽ được ghi vào "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)

Câu trả lời này không truy cập trực tiếp vào whale.txt hoặc whale2.txt. Nó sử dụng các thư viện nén tiêu chuẩn hiện có như được cho phép rõ ràng trong các quy tắc.


có thể có một "\ r \ n" trong đó tôi không thể thoát khỏi Windows khi tôi đếm chúng
Legorhin

2
vâng đó là một lỗi đánh máy được truyền bá
Legorhin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.