Nén và giải nén văn bản - Chưa bao giờ.


38

Với các cuộc thảo luận gần đây về việc sử dụng các công cụ nén trong mã golf , tôi nghĩ rằng sẽ là một thử thách tốt để viết trình nén và giải nén văn bản của riêng bạn.

Thử thách:

Viết hai chương trình : một để nén văn bản ASCII thành một chuỗi byte và một chương trình khác để giải nén nó. Các chương trình không cần phải có cùng ngôn ngữ.

Chương trình đầu tiên nên đọc một đoạn văn bản ASCII (từ tệp hoặc từ đầu vào tiêu chuẩn hoặc sử dụng bất kỳ cơ chế nào là tự nhiên nhất đối với ngôn ngữ) và xuất ra phiên bản nén của nó. (Đầu ra được nén có thể bao gồm hoặc các byte tùy ý; không cần đọc được.) Chương trình thứ hai sẽ đọc đầu ra của đầu tiên và tạo lại văn bản đầu vào ban đầu.

Ghi điểm:

Điểm của một giải pháp sẽ là tổng của ba lần đếm sau:

  1. Độ dài của chương trình máy nén tính bằng ký tự.
  2. Độ dài của đầu ra của máy nén, được đưa ra đầu vào thử nghiệm bên dưới, tính bằng byte.
  3. Độ dài của chương trình giải nén (nếu khác với máy nén) tính bằng ký tự.

Bạn nên lưu ý cả ba số đếm và tổng của chúng trong câu trả lời của bạn. Vì đây là mã golf, điểm càng thấp thì càng tốt.

Các quy tắc và hạn chế:

  • Bạn không được sử dụng bất kỳ công cụ hoặc thư viện nén hoặc giải nén có sẵn nào, ngay cả khi chúng đi kèm với ngôn ngữ bạn đã chọn. Nếu nghi ngờ về việc liệu một công cụ hoặc chức năng nhất định có được phép hay không, vui lòng hỏi.

  • Chương trình máy nén của bạn phải có khả năng xử lý đầu vào bao gồm bất kỳ văn bản ASCII có thể in nào , bao gồm các tab (ASCII 9) và nguồn cấp dữ liệu (ASCII 10). Bạn có thể, nhưng không bắt buộc, xử lý đầu vào Unicode và / hoặc nhị phân tùy ý.

  • Chương trình giải nén của bạn phải tạo ra chính xác cùng một đầu ra như được đưa cho máy nén làm đầu vào. Cụ thể, lưu ý không xuất nguồn cấp dữ liệu theo sau nếu đầu vào không có. (Đầu vào kiểm tra bên dưới không có nguồn cấp dữ liệu theo dõi, vì vậy bạn sẽ cần kiểm tra riêng điều này. Mẹo cho GolfScript : '':n.)

  • Máy nén và bộ giải nén của bạn có thể là cùng một chương trình (với chế độ thích hợp được chọn, ví dụ như với một công tắc dòng lệnh). Trong trường hợp đó, chiều dài của nó chỉ được tính một lần .

  • Các chương trình không được quá chậm hoặc đói bộ nhớ . Nếu nén hoặc giải nén đầu vào kiểm tra mất hơn một phút trên máy tính để bàn không quá mới của tôi (AMD Athlon64 X2 2.2GHz) hoặc tiêu thụ nhiều hơn một gigabyte RAM, tôi sẽ loại trừ giải pháp không hợp lệ. Những giới hạn này là cố tình lỏng lẻo - xin vui lòng cố gắng không đẩy chúng. (Xem sửa đổi bên dưới: bạn cần có khả năng xử lý ít nhất 100 kB đầu vào trong các giới hạn này.)

  • Mặc dù chỉ có vấn đề đầu vào kiểm tra để ghi điểm, nhưng ít nhất bạn nên nỗ lực nén văn bản đầu vào tùy ý. Một giải pháp đạt được tỷ lệ nén tốt chỉ cho đầu vào thử nghiệm, và không có gì khác, là hợp lệ về mặt kỹ thuật nhưng sẽ không nhận được một upvote từ tôi.

  • Các chương trình nén và giải nén của bạn nên được khép kín . Cụ thể, nếu họ phụ thuộc vào khả năng đọc một số tệp hoặc tài nguyên mạng không phải là một phần của môi trường thời gian chạy tiêu chuẩn của ngôn ngữ bạn đã chọn, thì độ dài của tệp hoặc tài nguyên đó sẽ được tính là một phần của độ dài của chương trình. (Điều này là không cho phép "máy nén" so sánh đầu vào với một tệp trên web và xuất ra byte không nếu chúng khớp. Xin lỗi, nhưng đó không phải là một thủ thuật mới nữa.)

Sửa đổi và làm rõ:

  • Máy nén của bạn phải có khả năng xử lý các tệp bao gồm ít nhất 100 kB văn bản tiếng Anh thông thường trong thời gian sử dụng bộ nhớ và thời gian hợp lý (tối đa một phút và một GB bộ nhớ). Bộ giải nén của bạn phải có khả năng giải nén kết quả đầu ra trong cùng giới hạn. Tất nhiên, việc có thể xử lý các tệp lâu hơn thế là hoàn toàn tốt và đáng khen ngợi. Bạn có thể phân chia các tệp đầu vào dài thành nhiều phần và nén chúng riêng lẻ hoặc sử dụng các phương tiện khác để đánh đổi hiệu quả nén cho tốc độ cho các đầu vào dài.

  • Máy nén của bạn có thể yêu cầu đầu vào của nó được cung cấp bằng cách sử dụng đại diện dòng mới gốc của nền tảng ưa thích của bạn (LF, CR + LF, CR, v.v.), miễn là bộ giải nén của bạn sử dụng cùng một đại diện dòng mới trong đầu ra của nó. Tất nhiên, máy nén cũng chấp nhận bất kỳ loại dòng mới nào (hoặc thậm chí chỉ dòng mới Unix bất kể nền tảng), miễn là bộ giải nén của bạn sau đó đưa ra loại dòng mới như trong đầu vào ban đầu.

Kiểm tra đầu vào:

Để đánh giá hiệu quả nén của các câu trả lời, đầu vào kiểm tra sau ( The Raven của Edgar Allan Poe, lịch sự của Dự án Gutenberg ) sẽ được sử dụng:

Once upon a midnight dreary, while I pondered, weak and weary,
Over many a quaint and curious volume of forgotten lore,
While I nodded, nearly napping, suddenly there came a tapping,
As of some one gently rapping, rapping at my chamber door.
"'T is some visiter," I muttered, "tapping at my chamber door--
                                          Only this, and nothing more."

Ah, distinctly I remember it was in the bleak December,
And each separate dying ember wrought its ghost upon the floor.
Eagerly I wished the morrow:--vainly I had sought to borrow
From my books surcease of sorrow--sorrow for the lost Lenore--
For the rare and radiant maiden whom the angels name Lenore--
                                          Nameless here for evermore.

And the silken sad uncertain rustling of each purple curtain
Thrilled me--filled me with fantastic terrors never felt before;
So that now, to still the beating of my heart, I stood repeating
"'T is some visiter entreating entrance at my chamber door
Some late visiter entreating entrance at my chamber door;--
                                          This it is, and nothing more."

Presently my soul grew stronger; hesitating then no longer,
"Sir," said I, "or Madam, truly your forgiveness I implore;
But the fact is I was napping, and so gently you came rapping,
And so faintly you came tapping, tapping at my chamber door,
That I scarce was sure I heard you"--here I opened wide the door;--
                                          Darkness there, and nothing more.

Deep into that darkness peering, long I stood there wondering, fearing,
Doubting, dreaming dreams no mortal ever dared to dream before;
But the silence was unbroken, and the darkness gave no token,
And the only word there spoken was the whispered word, "Lenore!"
This I whispered, and an echo murmured back the word, "Lenore!"
                                          Merely this and nothing more.

Back into the chamber turning, all my soul within me burning,
Soon again I heard a tapping, somewhat louder than before.
"Surely," said I, "surely that is something at my window lattice;
Let me see, then, what thereat is, and this mystery explore--
Let my heart be still a moment and this mystery explore;--
                                          'T is the wind and nothing more!"

Open here I flung the shutter, when, with many a flirt and flutter,
In there stepped a stately Raven of the saintly days of yore.
Not the least obeisance made he; not a minute stopped or stayed he;
But, with mien of lord or lady, perched above my chamber door--
Perched upon a bust of Pallas just above my chamber door--
                                          Perched, and sat, and nothing more.

Then this ebony bird beguiling my sad fancy into smiling,
By the grave and stern decorum of the countenance it wore,
"Though thy crest be shorn and shaven, thou," I said, "art sure no craven,
Ghastly grim and ancient Raven wandering from the Nightly shore,--
Tell me what thy lordly name is on the Night's Plutonian shore!"
                                          Quoth the Raven, "Nevermore."

Much I marvelled this ungainly fowl to hear discourse so plainly,
Though its answer little meaning--little relevancy bore;
For we cannot help agreeing that no living human being
Ever yet was blessed with seeing bird above his chamber door--
Bird or beast upon the sculptured bust above his chamber door,
                                          With such name as "Nevermore."

But the Raven, sitting lonely on the placid bust, spoke only
That one word, as if his soul in that one word he did outpour.
Nothing further then he uttered--not a feather then he fluttered--
Till I scarcely more than muttered, "Other friends have flown before--
On the morrow _he_ will leave me, as my hopes have flown before."
                                          Then the bird said, "Nevermore."

Startled at the stillness broken by reply so aptly spoken,
"Doubtless," said I, "what it utters is its only stock and store,
Caught from some unhappy master whom unmerciful Disaster
Followed fast and followed faster till his songs one burden bore--
Till the dirges of his Hope that melancholy burden bore
                                          Of 'Never--nevermore.'"

But the Raven still beguiling all my sad soul into smiling,
Straight I wheeled a cushioned seat in front of bird and bust and door;
Then, upon the velvet sinking, I betook myself to linking
Fancy unto fancy, thinking what this ominous bird of yore--
What this grim, ungainly, ghastly, gaunt and ominous bird of yore
                                          Meant in croaking "Nevermore."

This I sat engaged in guessing, but no syllable expressing
To the fowl whose fiery eyes now burned into my bosom's core;
This and more I sat divining, with my head at ease reclining
On the cushion's velvet lining that the lamplight gloated o'er,
But whose velvet violet lining with the lamplight gloating o'er
                                          _She_ shall press, ah, nevermore!

Then, methought, the air grew denser, perfumed from an unseen censer
Swung by seraphim whose foot-falls tinkled on the tufted floor.
"Wretch," I cried, "thy God hath lent thee--by these angels he hath sent thee
Respite--respite and nepenthe from thy memories of Lenore!
Quaff, oh quaff this kind nepenthe, and forget this lost Lenore!"
                                          Quoth the Raven, "Nevermore."

"Prophet!" said I, "thing of evil!--prophet still, if bird or devil!--
Whether Tempter sent, or whether tempest tossed thee here ashore,
Desolate yet all undaunted, on this desert land enchanted--
On this home by Horror haunted--tell me truly, I implore--
Is there--_is_ there balm in Gilead?--tell me--tell me, I implore!"
                                          Quoth the Raven, "Nevermore."

"Prophet!" said I, "thing of evil--prophet still, if bird or devil!
By that Heaven that bends above, us--by that God we both adore--
Tell this soul with sorrow laden if, within the distant Aidenn,
It shall clasp a sainted maiden whom the angels name Lenore--
Clasp a rare and radiant maiden whom the angels name Lenore."
                                          Quoth the Raven, "Nevermore."

"Be that word our sign of parting, bird or fiend!" I shrieked, upstarting--
"Get thee back into the tempest and the Night's Plutonian shore!
Leave no black plume as a token of that lie thy soul hath spoken!
Leave my loneliness unbroken!--quit the bust above my door!
Take thy beak from out my heart, and take thy form from off my door!"
                                          Quoth the Raven, "Nevermore."

And the Raven, never flitting, still is sitting, still is sitting
On the pallid bust of Pallas just above my chamber door;
And his eyes have all the seeming of a demon's that is dreaming,
And the lamplight o'er him streaming throws his shadow on the floor;
And my soul from out that shadow that lies floating on the floor
                                          Shall be lifted--nevermore!

Đầu vào kiểm tra chính xác (được mã hóa với các dòng mới theo kiểu Unix) phải dài 7043 byte và có hàm băm MD5 thập lục phân 286206abbb7eca7b1ab69ea4b81da227. ( md5sum -tsẽ tạo ra cùng một giá trị băm ngay cả khi bạn sử dụng các dòng mới CR + LF trên DOS / Windows.) Đầu ra của bộ giải nén của bạn phải có cùng độ dài và hàm băm.

Thi thiên Hãy nhớ rằng thử thách này chỉ khó như bạn thực hiện. Thực sự, bất cứ điều gì dưới 7043 được tính là một điểm tốt. (Ở đầu kia của thang điểm, tôi sẽ cực kỳ ấn tượng nếu có ai đạt được điểm dưới 2500.)


Vì vậy, tôi mang nó bạn không muốn thấy bất kỳ nén mất mát ?
Ông Llama

2
Lưu ý ưu tiên cho những người không thể có hàm băm MD5 phù hợp: tệp văn bản có dòng mới Unix cho phần cuối dòng. Ngoài ra, hãy chắc chắn rằng bạn có dòng mới cuối cùng trong tệp cho toàn bộ chiều dài 7043 byte.
Ông Llama

@GigaWatt: Vâng, tôi nên nói rõ hơn về các dòng mới. Vì tôi chỉ giới hạn đầu vào cho văn bản ASCII, tôi đoán tôi có thể cho phép mọi người sử dụng bất kỳ quy ước dòng mới nào cảm thấy tự nhiên nhất đối với họ, miễn là họ sử dụng nó một cách nhất quán. Tôi sẽ cố gắng nghĩ ra một cách hay để diễn đạt điều đó trong thử thách. Và không, máy nén không bị mất.
Ilmari Karonen

Làm thế nào về độ dài tệp, có bắt buộc phải chạy (trong thời gian chấp nhận được) chỉ đối với các tệp theo thứ tự kích thước của ví dụ hoặc cho các tệp lớn hơn nhiều (> một số MB) không?
đã ngừng quay ngược chiều

1
Nếu đầu ra được đưa ra dưới dạng một chương trình có cùng ngôn ngữ với máy nén, chúng ta có thể tính độ dài của bộ giải nén là 0 không?
Peter Taylor

Câu trả lời:


19

Perl, 3502 = 133 + 3269 + 100

Bộ mã hóa:

#!/usr/bin/perl -0
$_=<>;for$e(map{~chr}0..255){++$p{$_}for/..|.\G./gs;
%p=$s=(sort{$p{$a}<=>$p{$b}}keys%p)[-1];$d.=/\Q$e/?$/:s/\Q$s/$e/g&&$s}print$_,$d

Và bộ giải mã:

#!/usr/bin/perl -0777
sub d{($p=$d{$_})?d(@$p):print for@_}
sub r{%d=map{chr,ord($c=pop)&&[pop,$c]}0..255;&d}r<>=~/./gs

Đối với những người theo chủ nghĩa thuần túy muốn tránh sử dụng các công tắc dòng lệnh: Bạn có thể xóa dòng shebang và thêm $/=chr;vào bộ mã hóa và $/=$,;bộ giải mã để có được hiệu ứng tương tự. (Điều này sẽ mang lại số điểm lên tới 3510.)

Mã này sử dụng sơ đồ nén rất nguyên thủy:

  • Tìm bigram hai char xuất hiện thường xuyên nhất trong văn bản nguồn.
  • Thay thế bigram bằng một giá trị byte hiện không được sử dụng.
  • Lặp lại cho đến khi không còn các bigram lặp lại (hoặc không còn các giá trị byte không sử dụng nữa).

Ai đó ngoài kia có thể nhận ra đây là phiên bản đơn giản của nén "ghép lại" (viết tắt của các cặp đệ quy).

Đây không phải là một sơ đồ nén chung rất tốt. Nó chỉ làm tốt với những thứ như văn bản ASCII, nơi có rất nhiều giá trị byte không được sử dụng, và thậm chí sau đó nó thường nhận được không quá tỷ lệ 45-50%. Tuy nhiên, nó có lợi thế là có thể thực hiện được với tối thiểu mã. Bộ giải nén nói riêng có thể khá nhỏ gọn. (Hầu hết các ký tự trong tập lệnh giải mã của tôi là để lấy từ điển bigram.)

Đây là một phiên bản không mã hóa của mã:

#!/usr/bin/perl
use strict;
use warnings;
# Run with -d to decode.
if ($ARGV[0] eq "-d") {
    shift;
    $_ = join "", <>;
    my @in = split //;
    my %dict;
    foreach my $n (0 .. 255) {
        my $c = shift @in;
        $dict{chr $n} = [ $c, shift @in ] if ord $c;
    }
    sub decode {
        foreach (@_) {
            if ($dict{$_}) {
                decode(@{$dict{$_}});
            } else {
                print $_;
            }
        }
    }
    decode @in;
} else {
    $_ = join "", <>;
    my @dict;
    for (my $n = 255 ; $n >= 0 ; --$n) {
        my $symbol = chr $n;
        if (!/\Q$symbol/) {
            my %pop;
            ++$pop{$_} for /../gs, /(?!^)../gs;
            my $str = (sort { $pop{$b} <=> $pop{$a} } keys %pop)[0];
            s/\Q$str/$symbol/g;
            $dict[$n] = $str;
        }
    }
    for (0..255) { $dict[$_] ||= "\0" }
    print @dict, $_;
}

Một biểu thức trong bộ mã hóa golf yêu cầu giải thích, tôi nghĩ, và đó là (sort{$p{$a}<=>$p{$b}}keys%p)[-1], để có được khóa có giá trị cao nhất. Có vẻ như nó nên được viết như (sort{$p{$b}<=>$p{$a}}keys%p)[0], nó làm điều tương tự và ngắn hơn một ký tự. Lý do tôi không viết nó theo cách đó là vì nó làm thay đổi khóa được chọn trong trường hợp khi có nhiều khóa có giá trị cao nhất. Do tình cờ, điều này khiến đầu ra kết quả cho đầu vào thử nghiệm dài hơn 10 byte. Tôi ghét phải đảm nhận nhân vật phụ vô dụng, nhưng không đủ để hy sinh 9 điểm từ điểm số của tôi.

Trong khuôn mặt của bạn, Golfscript! (Haha, Golfscript sẽ hoàn toàn đến đây và đá vào mông tôi nếu nó có thể nghe thấy tôi.)


3
Wow, thật ấn tượng! Thi thiên Đây dường như là câu trả lời thường được chấp nhận liên quan đến việc đếm các công tắc dòng lệnh.
Ilmari Karonen

Dang, tôi đã đọc nó sớm hơn nhưng tôi không nhận thấy bit đó ở giữa. Nghe có vẻ giống như upshot là: bạn không đếm ký tự dấu gạch nối ban đầu (vì bạn chỉ có thể thêm nó vào -egói tùy chọn), trừ khi mã của bạn chứa một ký tự trích dẫn, trong trường hợp đó bạn sẽ đếm dấu gạch nối (vì bây giờ bạn phải chạy nó từ một tệp có dòng shebang để tránh phải trả tiền cho việc thoát khỏi trích dẫn đơn trên dòng lệnh).
hộp bánh mì

1
Kỹ thuật còn được gọi là mã hóa cặp Byte . Triển khai tốt đẹp
roblogic

@roblogic Cảm ơn bạn đã tham khảo; đó là điều tốt để biết
hộp bánh mì

20

Con trăn, 3514 = 294 + 2894 + 326

Về cơ bản là một triển khai bzip2 . Nó thực hiện một phép biến đổi Burrows-Wheeler , một phép biến đổi di chuyển về phía trước , một Huffman đơn giản mã hóa thành một luồng bit, chuyển đổi luồng bit đó thành một số nguyên và ghi ra các byte.

Mã hoá:

import sys
S=range(128)
H={0:'0'}
for b in range(7):
 for i in range(1<<b,2<<b):H[i]='1'*b+'10'+bin(i)[3:]
I=sys.stdin.read()+'\0'
N='1'
for x in sorted(I[i:]+I[:i]for i in range(len(I))):i=S.index(ord(x[-1]));N+=H[i];S=[S[i]]+S[:i]+S[i+1:]
N=int(N,2)
while N:sys.stdout.write(chr(N%256));N>>=8

Slà hàng đợi di chuyển về phía trước, Hlà bộ mã hóa Huffman và Nlà dòng bit.

Việc mã hóa làm giảm đầu vào kiểm tra xuống khoảng 41% kích thước ban đầu của nó.

Bộ giải mã:

import sys
N=0
b=1
for c in sys.stdin.read():N+=ord(c)*b;b<<=8
N=bin(N)[3:]
S=range(128)
L=''
while N:
 n=N.find('0')
 if n:i=2**n/2+int('0'+N[n+1:2*n],2);N=N[2*n:]
 else:i=0;N=N[1:]
 L+=chr(S[i]);S=[S[i]]+S[:i]+S[i+1:]
S=''
i=L.find('\0')
for j in L:S=L[i]+S;i=L[:i].count(L[i])+sum(c<L[i]for c in L)
sys.stdout.write(S[:-1])

1
Tôi đã cố gắng thực hiện BWT và thực hiện một hình thức nén thực sự nhưng lại quá lười biếng. : P
Ông Llama

8

Trình biên dịch 8086 / MS_DOS

Máy nén khí: 155

jNiAxBCO2I7AM/+9/QW5AAGK2TPAq4rDqv7D4va6AQkz9lK0BrL/zSFadDK7
/f+DwwM733QNOTd19ThHAnXwid7r34k1iEUC6BMAtACKRQJr8AODxwPryrQC
zSHrxFIz0ovGuwMA9/Nai9iKztPL0ePQ0nMWgPr+cgtSsv60Bs0hWoDq/rQG
zSGyAf7JdeA5/XUHA+2DxQP+xsM=

Dữ liệu: 3506

Bộ giải nén: 203

ieWD7CCM2IDEEI7YjsAz/7kAAYrZM8CrisOq/sPi9rYJxkb0Abn9BehtAIl2
/uhTAOhkAIl28Dv3cy3oRgCLRv6JBYt28Il2/oM8AHQEizTr94pEAohFAoPH
AznPddL+xgPJg8ED68mLdv6JNYM8AHQEizTr94pEAohFAol+/on+aFgBgzwA
dAdWizTo9f9etAaKVALNIcMz9ojz/k70dRu0BrL/zSF0IDz+cgi0BrL/zSEE
/sZG9AiIRvLQZvLR1v7Ldddr9gPDzSA=

Tổng cộng: 3864

Sử dụng bộ giải mã Base64 này và lưu các tệp nhị phân dưới dạng 'compression.com' và 'decompress.com' và sau đó làm:

compress < source > compressed_file
decompress < compressed_file > copy_of_source

trong một vỏ DOS (được thử nghiệm với WinXP). Không có lỗi kiểm tra nên việc nén các tệp lớn sẽ tạo ra kết quả không chính xác. Một vài bổ sung nhỏ và nó có thể đối phó với bất kỳ tệp có kích thước nào. Ngoài ra, nó không thể giải nén thành nhị phân vì nó không thể tạo ra giá trị 0xff (dữ liệu nén thoát khỏi giá trị 0xff là 0xfe 0xff với 0xfe thoát là 0xfe 0xfe). Sử dụng tên tệp dòng lệnh sẽ khắc phục vấn đề đầu ra nhị phân, nhưng sẽ là một thực thi lớn hơn.


Chương trình sử dụng loại thuật toán nén nào?
Sir_Lagsalot

@Sir_Lagsalot: Nó sử dụng LZW có chiều rộng bit thay đổi (cái được sử dụng trong tệp GIF).
Skizz

6

Bài thơ Bash (566 + 117) + 4687 = 5370

Để giải trí, tôi đã cải trang một máy nén như một bài thơ:

for I in my chamber nodded, nearly napping, suddenly heard rapping, tapping upon my door    \
"'T is some visiter" \ I\  muttered, o\'er lamplight "nothing more" \
just this sainted maiden whom the angels name Lenore    \
And "Prophet!" said me "thing of evil" -- "prophet still, if bird or devil!"    \
Leave no token of that lie thy soul hath spoken and sitting take thy ore from This floor    \
But you velvet bird from some shore above   \
here this with sad raven before his word still spoke nothing    \
"                                          " Quoth the Raven Never more;                    do C=$[C+1];E=`perl -e "print chr($C+128)"`;echo "s/$I/$E/g">>c;echo "s/$E/$I/g">>d;done;LANG=C sed -f $1;rm c d

Đây là một máy nén hợp nhất: chạy với tùy chọn "c" nó sẽ nén và với "d" nó sẽ giải nén. Nó có hai phần: phiên bản "đọc tiêu hóa" 566 byte của bài thơ và (2) hậu tố 117 byte trong đó tất cả các bash "thực" được thực hiện.

Với sự cẩn thận (ví dụ: bắt đầu bài thơ bằng "cho tôi vào") bash sẽ diễn giải phiên bản "mất mát" của bài thơ như một mảng. Nó thay thế từng phần tử của mảng bằng ký tự không phải ASCII (chúng tôi giả sử đầu vào là ASCII nên không có xung đột). Một lợi thế nhỏ của giải pháp này: vì chúng tôi sử dụng thực tế là chúng tôi có thể giả sử đầu vào là ASCII, đầu ra của nén này sẽ không bao giờ dài hơn đầu vào của nó, bất kể phần đầu vào và / hoặc mất mát là gì.

Quy tắc này gần nhất với vi phạm là quy tắc về việc cung cấp tỷ lệ nén kha khá trên các văn bản khác. Tuy nhiên, Nó loại bỏ 1386 byte khỏi văn bản GPL V2, vượt quá kích thước của chính nó, có vẻ phù hợp với định nghĩa của OP decent. Do đó, nó dường như cung cấp cái gọi là decentnén trên các văn bản chung. Điều này là do khá nhiều bất kỳ văn bản tiếng Anh nào cũng sẽ có "" "đó". Rõ ràng nó sẽ hoạt động tốt hơn nếu bạn thay thế phần "mất mát" bằng văn bản giống với bản gốc mà bạn muốn nén một cách dễ dàng.

Chia hình ảnh và âm thanh thành các phần mất mát và không mất mát là một kỹ thuật được biết đến. Điều này cũng không hoạt động tốt đối với văn bản: 4687 byte không phải là tuyệt vời ngay cả khi chúng tôi loại trừ 566 byte khỏi phiên bản mất dữ liệu và chúng tôi thực sự không thể tự động tạo ra một phiên bản văn bản mất dữ liệu giống như chúng tôi có thể cho âm thanh. Về mặt tích cực, điều này có nghĩa là mỗi khi bạn nén thứ gì đó bằng máy nén này, bạn có thể vui vẻ tạo ra một phiên bản mất mát bằng tay. Vì vậy, đây có vẻ như là một giải pháp "cho vui" hợp lý.


5

C ++, 4134 byte (mã = 1357, nén = 2777)

Điều này thực hiện một phép biến đổi Burrows-Wheeler + Move-To-Front như của Keith Randall, nhưng sau đó nén chuỗi byte kết quả bằng cách sử dụng Bộ giải mã phạm vi thích ứng . Thật không may, nén được cải thiện từ bộ mã hóa phạm vi không đủ để bù đắp cho tính dài dòng của C ++. Tôi có thể đánh golf mã này thêm một số, cụ thể là sử dụng một phương thức nhập / xuất khác, nhưng nó sẽ không đủ để đánh bại các bài nộp khác với thuật toán hiện tại. Mã này là Windows cụ thể và chỉ hỗ trợ văn bản ascii.
Để nén: "C text_file compression_file"
Để giải nén: "D compression_file unsompression_file "
Khá nhiều lỗi dòng lệnh hoặc lỗi tệp sẽ làm hỏng chương trình và mất một phút tốt hơn để mã hóa hoặc giải mã bài thơ.

#include <windows.h>
#include <algorithm>
typedef DWORD I;typedef BYTE u;
#define W while
#define A(x)for(a=0;a<x;a++)
#define P(x)*o++=x;
I q,T=1<<31,B=T>>8,a,l,f[257],b,G=127,p=G,N=255;I Y(u*i,u*j){return
memcmp(i,j,l)<0;}I E(u*i,u*o){b=0;I L=0,h=0,R=T;u*c=o,*e=i+l;W(i<e){I
r=R/p,s=0;A(*i)s+=f[a];s*=r;L+=s;R=*i<N?r*f[*i++]++:R-s;p++;W(R<=B){if((L>>23)<N){for(;h;h--)P(N)P(L>>23)}else{if(L&T){o[-1]++;for(;h;h--)P(0)P(L>>23)}else
h++;}R<<=8;L<<=8;L&=T-1;}}P(L>>23)P(L>>15)P(L>>7)return
o-c;}void D(u*i,u*o){I R=128,L=*i>>1;u*e=o+l;W(o<e){W(R<=B){L<<=8;L|=((*i<<7)|(i++[1]>>1))&N;R<<=8;}I
h=R/p,m=L/h,x=0,v=0;W(v<=m)v+=f[x++];P(--x);L-=h*(v-f[x]);R=h*f[x]++;p++;}}void
main(I Z,char**v){u d[1<<16];I c=*v[1]<68,s;HANDLE F=CreateFileA(v[2],T,0,0,3,0,0),o=CreateFileA(v[3],T/2,0,0,2,0,0);ReadFile(F,d,GetFileSize(F,0),&l,0);l=c?l:*(I*)d;A(G)f[a]=1;u M[256];A(G)M[a]=a+1;u*g=new u[l*3],*h=g+l;if(c){memcpy(d+l,d,l);u**R=new
u*[l];A(l)R[a]=d+a;std::sort(R,R+l,Y);A(l){b=R[a][l-1];I
i=strchr((char*)M,b)-(char*)M;memmove(M+1,M,i);*M=g[a]=b;h[a]=i;}s=E(h,d+l+8);}else{D(d+8,g);A(l){I
k=g[a];g[a]=M[k];memmove(M+1,M,k);*M=g[a];}}u**j=new u*[l];A(l)j[a]=new
u[l*2],memset(j[a],0,l*2),j[a]+=l;A(l){for(b=0;b<l;)*--j[b]=g[b++];std::sort(j,j+l,Y);}if(c){A(l){if(!memcmp(j[a],d,l)){I*t=(I*)(d+l);*t=l;t[1]=a;g=d+l,l=s+8;}}}else
g=j[*(I*)(d+4)];WriteFile(o,g,l,&q,0);}

5

JavaScript, 393 (mã) + 3521 (thử nghiệm) = 3914 (tổng cộng)

Chương trình này lặp đi lặp lại thay thế các giá trị byte không sử dụng cho các đoạn 2 đến 4 ký tự của đầu vào. Mỗi lần thay thế được tính dựa trên tần số và độ dài của đoạn gốc và sự thay thế tốt nhất được chọn mỗi lần. Tôi sẽ thêm một giai đoạn mã hóa Huffman cuối cùng nếu tôi có thể tìm ra cách thực hiện nó với số lượng ký tự tương đối nhỏ. Giải nén về cơ bản là một loạt các hoạt động tìm và thay thế.

Sử dụng

C () cung cấp nén; U () cung cấp giải nén. Vì các chuỗi của JavaScript dựa trên các đơn vị mã Unicode 16 bit, chỉ 8 bit đáng kể nhất của mỗi đơn vị mã được sử dụng ở định dạng dữ liệu nén; điều này tương thích với các hàm btoa () và atob () của Firefox để mã hóa Base64. ( ví dụ sử dụng )

Chương trình này chỉ có thể hoạt động trong Firefox do tùy chọn "g" không chuẩn thành .replace ().

Mã đánh gôn:

S=String.fromCharCode;function C(c){h=[];for(f=0;129>f;++f){g='';i=0;for(e=2;5>e;++e){d={};for(a=0;a<=c.length-e;a+=e)b="K"+c.substr(a,e),d[b]=d[b]?d[b]+1:1;for(b in d)a=d[b],a=a*e-(1+e+a),a>i&&(g=b.slice(1),i=a)}if(!g)break;h[f]=g;c=c.replace(g,S(127+f),"g")}return h.join("\1")+"\1"+c}function U(a){c=a.split("\1");a=c.pop();for(b=c.length,d=127+b;b--;)a=a.replace(S(--d),c[b],"g");return a}

Trước khi chơi golf:

function compress(str) {

    var hash, offset, match, iteration, expansions, bestMatch, bestScore, times, length, score;

    expansions = [];

    for (iteration = 0; iteration < 129; ++iteration) {

        bestMatch = null;
        bestScore = 0;

        for (length = 2; length < 5; ++length) {

            hash = {};

            for (offset = 0; offset <= str.length - length; offset += length) {
                match = 'K' + str.substr(offset, length);
                hash[match] = hash[match] ? hash[match] + 1 : 1;
            }

            for (match in hash) {
                times = hash[match];
                score = times * length - (1 + length + times);
                if (score > bestScore) {
                    bestMatch = match.slice(1);
                    bestScore = score;
                }
            }

        }

        if (!bestMatch) {
            break;
        }

        expansions[iteration] = bestMatch;
        str = str.replace(bestMatch, String.fromCharCode(127 + iteration), 'g');

    }

    return expansions.join('\u0001') + '\u0001' + str;
}

function uncompress(str) {
    var i, j, expansions;

    expansions = str.split('\u0001');
    str = expansions.pop();

    for (j = expansions.length, i = 127 + j; j--;) {
        str = str.replace(String.fromCharCode(--i), expansions[j], 'g');
    }

    return str;
}

Tại sao tôi nhận được C(text).length=7301? (FF 60.0.2)
l4m2

3

PHP, (347 + 6166 + 176) = 6689

Vì vậy, tôi đã đi với một từ điển đơn giản + phương pháp thay thế.

Nếu một từ xuất hiện nhiều lần và nó ngắn hơn (mã hóa từ + lưu mục thay thế) thì nó sẽ thay thế. Nếu "từ" xảy ra là một số, thì dù sao đi nữa, nó sẽ ngăn chặn sự thay thế ngẫu nhiên trong quá trình giải nén. "Từ điển" của các thay thế được nối bởi các byte null, theo sau là hai byte null, theo sau là phần thân thay thế hoạt động.

Những cải tiến có thể có:

  • Windows không thích đường ống hơn 4kb dữ liệu xung quanh, vì vậy hãy tìm cách tốt hơn so với sử dụng tệp.
  • Khả năng khớp các chuỗi khoảng trắng dài và tính chúng là "từ" mà không cần thêm quá nhiều mã.
  • Đến với một cái gì đó thay thế tốt hơn thay vì sử dụng số.

Cách sử dụng: máy nén tìm kiếm một tệp có tên "i" và ghi dữ liệu nén vào "o". Bộ giải nén tìm kiếm "o" và ghi dữ liệu không nén vào "d". Đây là cách giải quyết tồi tệ của tôi đối với Windows không thích các ống dữ liệu xung quanh.


nén.php (347)

<?$d=file_get_contents('i');$z=chr(0);preg_match_all('|\b(\w+)\b|',$d,$m);$n=0;foreach($m[0]as$w){$l=strlen($w);$q[$w]=isset($q[$w])?$q[$w]+$l:$l;}arsort($q);foreach($q as$w=>$s){$l=strlen($w);$c=$s/$l;if($c*strlen($n)+$l<$s||is_int($w)){$d=preg_replace('|\b'.preg_quote($w).'\b|',$n++,$d);$f[]=$w;}}file_put_contents('o',implode($z,$f).$z.$z.$d);

Phiên bản mở rộng với ý kiến ​​và giải thích.


Mẫu đầu ra không có từ điển. Kinda buồn cười khi nhìn vào.
Kích thước bình thường: 6166 .

Ah, distinctly I remember it 45 in 0 bleak December,
25 each separate dying ember wrought its ghost 39 0 37.
Eagerly I wished 0 88:--vainly I had sought to borrow
From 9 books surcease of 43--43 for 0 lost 8--
For 0 rare 1 67 40 54 0 26 38 8--
                                          Nameless 63 for evermore.

25 0 silken sad uncertain rustling of each purple curtain
Thrilled me--filled me 19 fantastic terrors never felt 17;
So 4 now, to 13 0 beating of 9 64, I stood repeating
"'T is 57 31 36 49 at 9 2 5
Some late 31 36 49 at 9 2 5;--
                                          58 it is, 1 10 16."

giải nén.php (176)

<?$z=chr(0);$d=file_get_contents('o');list($w,$d)=explode($z.$z,$d);$w=explode($z,$w);$n=0;foreach($w as$r){$d=preg_replace('|\b'.$n++.'\b|',$r,$d);};file_put_contents('d',$d);

Phiên bản mở rộng với lời giải thích.


Bất kỳ đề xuất để cải thiện chào đón.

Chỉnh sửa: Đã thêm các phiên bản "chưa được kiểm soát" của mã và thêm hàng tấn ý kiến. Nên dễ theo dõi.


Trời ạ! Ngôn ngữ và phương pháp tương tự như tôi đang sử dụng! Chết tiệt. Mặc dù tôi đã không bỏ qua những từ đơn lẻ.
Gareth

Điều gì xảy ra khi có số trong văn bản? cuối cùng nó sẽ thay thế các số ban đầu bằng một từ không đúng chỗ. Mặc dù tôi đã thực hiện một cách tiếp cận tương tự (chia nhỏ regex, tìm các từ phổ biến để thay thế và tạo từ điển thay thế và dán nó bằng null), tôi đã sử dụng các ký tự unicode thay vì số (bắt đầu từ chr (128), vì mọi thứ sau đó không thể in được ascii tiêu chuẩn)
Blazer

@Blazer: Trên thực tế, có mã (cụ thể ||is_int($w)) để xử lý các số bằng cách luôn thêm chúng vào từ điển, nhưng dường như là lỗi: sau khi nén và giải nén toàn bộ văn bản E-Gutenberg, đầu ra bắt đầu bằng The 4 3 EBook 2 The Raven, by Edgar Allan Poe. :-( Tôi nghi ngờ vấn đề là thứ gì đó đang được thay thế hai lần; bạn có thể muốn cân nhắc sử dụng strtr()thay vì để tránh vấn đề đó.
Ilmari Karonen

@Ilmari nếu bạn có một tài liệu nặng về số lượng, việc thêm các số đó vào từ điển có thể dẫn đến việc nén lớn hơn ban đầu. để lưu trữ một vài vật phẩm dài 1-2 ký tự không hiệu quả. giống như nếu bạn thay thế từ 'a' trong tài liệu
Blazer

@Blazer - Đối với tất cả các thuật toán nén, có một số đầu vào nhất định sẽ dẫn đến đầu ra lớn hơn . Đó là vốn có trong nén không mất dữ liệu, giống như không có khả năng nén dữ liệu entropic đáng tin cậy.
Ông Llama

3

GolfScript, 3647 (kích thước nén 3408 + kích thước mã 239)

128,{[.;]''+}%:d;8:k;{2k?={1k+:k;}*}:|;{2base}:b;{.[0]*@b+0@->}:$;.0=
{'':&,:i;1/{.d&@+?.0<{;d,i@d&@:&.0=:i;[+]+:d;k$\|}{:i;&\+:&;}if}%[0]k*+[]*8/{b}%"\0"\+}
{1>{8$}/][]*:^;{^k<b^k>:^;}:r~{.}{d,|d=:&r..d,<{d=}{;&}if[1<&\+]d\+:d;}while;}if

Thuật toán được sử dụng là nén LZW với các mã có chiều rộng thay đổi. Dòng đầu tiên là mã được chia sẻ, dòng thứ hai là mã nén và dòng thứ ba là mã giải nén.

Nó xử lý các tệp có ký tự ASCII trong phạm vi 1-127 và nó tự động nhận ra các tệp nén (chúng bắt đầu bằng 0 byte), do đó không cần tham số để giải nén.

Chạy ví dụ:

$ md5sum raven.txt
286206abbb7eca7b1ab69ea4b81da227  raven.txt
$ ruby golfscript.rb compress.gs < raven.txt > raven.lzw
$ ls -l raven.lzw
-rw-r--r-- 1 ahammar ahammar 3408 2012-01-27 22:27 raven.lzw
$ ruby golfscript.rb compress.gs < raven.lzw | md5sum
286206abbb7eca7b1ab69ea4b81da227  -

Lưu ý: Tôi đã bắt đầu từ lâu trước khi yêu cầu xử lý 100kb được thêm vào, vì vậy tôi đã không kiểm tra nó trên đầu vào có kích thước đó. Tuy nhiên, phải mất khoảng 30 giây để nén đầu vào thử nghiệm và 5 giây để giải nén nó, sử dụng khoảng 20 MB bộ nhớ ở mức cao nhất.


Nén một tệp 76 kB dường như mất khoảng 19 phút, trong khi giải nén nó mất 10. Đó loại chậm, nhưng một lần nữa, nó lại vượt qua các quy tắc ban đầu, vì vậy ... tôi không biết. Có vẻ như không công bằng khi không cho phép nó trong hoàn cảnh. Tôi đoán tôi có thể gọi một "mệnh đề ông nội" cho bạn hoặc một cái gì đó.
Ilmari Karonen

3

Haskell, 3973

Đến bữa tiệc muộn, và sẽ không giành chiến thắng, nhưng tôi rất vui khi viết nó để tôi cũng có thể đăng nó.

Đây là một triển khai LZW có chiều rộng thay đổi đơn giản, với một từ điển được giới hạn rõ ràng với ASCII, tab và nguồn cấp dữ liệu có thể in được. Chạy không có đối số, nó nén đầu vào tiêu chuẩn vào tập tin C. Chạy với bất kỳ đối số nào (nhưng "- giải nén" sẽ là một đặt cược hợp lý), nó giải nén tệp Cthành đầu ra tiêu chuẩn.

import List
import System
import Data.Binary
q=head
main=getArgs>>=m
m[]=getContents>>=encodeFile"C".s 97 128 1 0.e 97h
m _=decodeFile"C">>=putStr.d tail""96h.u 97 128
h=zip[0..].map(:[])$"\t\n"++[' '..'~']
e _ _[]=[]
e n s y=c:e(n+1)((n,take(1+l)y):s)(drop(l)y)where{Just(c,p)=find((`isPrefixOf`y).snd)s;l=length p}
d _ _ _ _[]=""
d f p n s(x:y)=t++d id t(n+1)(f$(n,p++[q t]):s)y where t=maybe(p++[q p])id$lookup x s
s _ _ _ a[]=a::Integer
s n w o a y|n>w=s n(2*w)o a y|0<1=s(n+1)w(o*w)(a+o*q y)(tail y)
u _ _ 0=[]
u n w x|n>w=u n(2*w)x|0<1=(x`mod`w::Integer):u(n+1)w(x`div`w)
  • kích thước mã: 578
  • cỡ mẫu nén: 3395
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.