Thử thách nén văn bản tiếng Anh lossless [đã đóng]


12

Thử thách:

Thách thức của bạn (nếu bạn chọn chấp nhận nó) là nén và giải nén " Tác phẩm hoàn chỉnh của William Shakespeare " 5 MB như được tìm thấy ở đây: http://www.gutenberg.org/cache/epub/100/pg100.txt

(MD5 a810f89e9f8e213aebd06b9f8c5157d8:)

Quy tắc:

  • Bạn phải nhận đầu vào qua STDINvà đầu ra qua STDOUT...
  • ... Và bạn phải cung cấp kết quả giải nén giống hệt cho đầu vào.
    • (Điều này là để nói rằng bạn phải có khả năng cat inpt.txt | ./cmprss | ./dcmpress | md5và nhận được cùng một MD5 như trên.)
    • (Bất cứ điều gì thông qua STDERRsẽ được loại bỏ.)
  • Bạn phải sử dụng ít hơn 2048 ký tự cho tổng mã nguồn của mình.
    • (Đây là không mã golf. Bạn không được tính điểm dựa trên độ dài của mã nguồn. Đây chỉ là một quy tắc để giữ cho mọi thứ hữu hạn.)
    • (Lấy độ dài nối của tất cả các mã nguồn nếu bạn đã tách nó ra.)
  • Bạn cũng phải có khả năng (về mặt lý thuyết) xử lý các đầu vào văn bản đơn giản tương tự.
    • (ví dụ: mã hóa cứng một cơ chế chỉ có khả năng xuất ra đầu vào Shakespeare được cung cấp là không thể chấp nhận được.)
    • (Kích thước nén của các tài liệu khác là không liên quan - miễn là kết quả giải nén giống hệt với đầu vào thay thế.)
  • Bạn có thể sử dụng bất kỳ lựa chọn ngôn ngữ nào.
    • (ví dụ: thoải mái nén bằng cách sử dụng awkvà giải nén bằng cách sử dụng java)
  • Bạn có thể viết hai chương trình riêng biệt hoặc kết hợp chúng với một số hình thức "chuyển đổi" nếu muốn.
    • (Phải có trình diễn rõ ràng về cách gọi cả chế độ nén và giải nén)
  • Bạn không được sử dụng bất kỳ lệnh bên ngoài nào (ví dụ thông qua exec()).
    • (Nếu bạn đang sử dụng ngôn ngữ shell - xin lỗi. Bạn sẽ phải thực hiện với phần mềm tích hợp. Bạn có thể đăng câu trả lời "không thể chấp nhận" để chia sẻ và thưởng thức - nhưng nó sẽ không bị đánh giá! )
  • Bạn không được sử dụng bất kỳ chức năng tích hợp hoặc thư viện nào được cung cấp với mục đích đã nêu là để nén dữ liệu (như gz, v.v.)
    • (Thay đổi mã hóa không được coi là nén trong ngữ cảnh này. Một số quyết định có thể được áp dụng ở đây. Vui lòng tranh luận về sự chấp nhận của giải pháp của bạn trong bài nộp.)
  • Hãy cố gắng vui vẻ nếu chọn tham gia!

Tất cả các cuộc thi tốt có một định nghĩa khách quan về chiến thắng; ergo:

  • Với tất cả các quy tắc được tuân thủ, đầu ra được nén nhỏ nhất (tính bằng STDOUTbyte) sẽ thắng.
    • (Báo cáo đầu ra của bạn xin vui lòng thông qua ./cmprss | wc -c)
  • Trong trường hợp bốc thăm (kích thước đầu ra giống hệt nhau), cộng đồng được bầu chọn nhiều nhất.
  • Trong trường hợp bốc thăm lần thứ hai (phiếu bầu cộng đồng giống hệt nhau), tôi sẽ chọn một người chiến thắng dựa trên kiểm tra hoàn toàn chủ quan về sự thanh lịch và thiên tài thuần túy. ;-)

Cách nộp:

Vui lòng định dạng mục nhập của bạn bằng cách sử dụng mẫu này:

<language>, <compressed_size>
-----------------------------

<description>  (Detail is encouraged!)

    <CODE...
    ...>

<run instructions>

Tôi sẽ khuyến khích độc giả và người gửi trò chuyện qua các bình luận - Tôi tin rằng có cơ hội thực sự để mọi người học hỏi và trở thành lập trình viên tốt hơn thông qua codegolf.stack.

Chiến thắng:

Tôi sẽ đi nghỉ sớm: Tôi có thể (hoặc không) theo dõi các bài nộp trong vài tuần tới và sẽ rút ra thách thức vào ngày 19 tháng 9. Tôi hy vọng điều này mang đến một cơ hội tốt để mọi người suy nghĩ và gửi - và để chia sẻ tích cực về các kỹ thuật và ý tưởng.

Nếu bạn đã học được điều gì mới từ việc tham gia (với tư cách là người đọc hoặc người gửi), vui lòng để lại nhận xét khuyến khích.


1
Bạn nên gắn thẻ này code-challenge.
kirbyfan64sos

1
Là lấy đầu vào như là một đối số chức năng được phép? Ví dụ: Một giải pháp trong các ngôn ngữ như JavaScript không thể chạy từ dòng lệnh, AFAIK. Trong trường hợp của tôi, nó sẽ đơn giản hơn nhiều để chạy trong trình duyệt.
Sản phẩm ETH

1
Tại sao mẫu? Bạn sẽ tạo một đoạn trích phụ thuộc vào nó?
Peter Taylor

2
Nếu không có giới hạn kích thước mã, điều gì ngăn tôi viết chương trình nén in 0 byte và chương trình giải nén được mã hóa cứng để in toàn bộ tác phẩm của Shakespeare?
Lynn

4
Một quy tắc có thể được thêm vào nói rằng mã về mặt lý thuyết nên hoạt động với các đầu vào khác, điều này giải quyết vấn đề @Mauris đã chỉ ra.
kirbyfan64sos

Câu trả lời:


5

Perl 5, 3651284

Chỉ là một lược đồ từ điển dựa trên từ đơn giản. Phân tích tần số từ của kho văn bản và sử dụng tần số đó để xác định xem có nên sử dụng một hoặc hai byte phí trên mỗi từ hay không. Sử dụng hai ký hiệu đặc biệt cho byte \ 0 và \ 1 vì chúng không xuất hiện trong kho văn bản. Có rất nhiều biểu tượng khác có thể được sử dụng. Điều này đã không được thực hiện. Không thực hiện bất kỳ mã hóa huffman hoặc bất kỳ nhạc jazz nào.

Kịch bản nén shakespeare.pl:

use strict;
use warnings;
use bytes;

my $text = join "", <>;
my @words = split/([^a-zA-Z0-9]+)/, $text;


my %charfreq;
for( my $i = 0; $i<length($text); ++$i ) {
    $charfreq{ substr($text, $i, 1) }++
}
for my $i ( 0..255 ) {
    my $c = chr($i);
    my $cnt = $charfreq{$c} // 0;
}



my %word_freq;
foreach my $word ( @words ) {
    $word_freq{ $word }++;
}


my $cnt = 0;
my ( @dict, %rdict );
foreach my $word ( sort { $word_freq{$b} <=> $word_freq{$a} || $b cmp $a } keys %word_freq ) {
    last if $word_freq{ $word } == 1; 


    my $repl_length = $cnt < 127 ? 2 : 3;
    if( length( $word ) > $repl_length ) {
        push @dict, $word;
        $rdict{ $word } = $cnt;
        $cnt++;
    }
}


foreach my $index ( 0..$
    print "$dict[$index]\0";
}
print "\1";


foreach my $word ( @words ) {
    my $index = $rdict{ $word };
    if ( defined $index && $index <= 127 ) {
        print "\0" . chr( $index );
    } elsif ( defined $index ) {
        my $byte1 = $index & 127;
        my $byte2 = $index >> 7;
        print "\1" . chr( $byte2 ) . chr( $byte1 );
    } else {
        print $word;
    }
}

Kịch bản giải nén deshakespeare.pl:

use strict;
use warnings;
use bytes;

local $/;
my $compressed = <>;
my $text = $compressed;
$text =~ s/^.+?\x{1}//ms;
my $dictionary = $compressed;
$dictionary =~ s/\x{1}.*$//ms;


my $cnt = 0;
my @dict;
foreach my $word ( split "\0", $dictionary ) {

    push @dict, $word;
}


my @words = split /(\x{0}.|\x{1}..)/ms, $text;
foreach my $word ( @words ) {
    if( $word =~ /^\x{0}(.)/ms ) {
        print $dict[ ord( $1 ) ];
    } elsif( $word =~ /^\x{1}(.)(.)/ms ) {
        my $byte1 = ord( $1 );
        my $byte2 = ord( $2 );
        my $index = ( $byte1 << 7 ) + $byte2;
        print $dict[ $index ];
    } else {
        print $word;
    }
}

Chạy bằng cách sử dụng:

perl shakespeare.pl < pg100.txt >pg100.txt.compressed
perl deshakespeare.pl <pg100.txt.compressed >pg100.txt.restored
diff pg100.txt pg100.txt.restored
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.