텍스트 압축 및 압축 해제 —“Nevermore”


38

코드 골프에서 압축 도구를 사용하는 것에 대한 최근의 논의와 함께 , 나는 당신 자신의 텍스트 압축기와 압축 해제기를 작성하는 것이 좋은 도전이라고 생각했습니다.

도전:

두 개의 프로그램을 작성하십시오 . 하나는 ASCII 텍스트를 바이트 시퀀스로 압축하고 다른 하나는 압축 해제합니다. 프로그램은 같은 언어 일 필요는 없습니다.

첫 번째 프로그램은 파일 또는 표준 입력에서 또는 언어에 가장 자연스러운 메커니즘을 사용하여 ASCII 텍스트를 읽고 압축 된 버전을 출력해야합니다. (압축 된 출력은 임의의 바이트로 구성 될 수 있으며 읽을 수는 없습니다.) 두 번째 프로그램은 첫 번째의 출력을 읽고 원래 입력 텍스트를 다시 작성해야합니다.

채점 :

해의 점수 는 다음 세 가지 수 의 입니다.

  1. 압축기 프로그램 의 길이 ( 문자)입니다.
  2. 출력 기간 바이트 테스트 입력 아래 압축기.
  3. 압축 해제 프로그램 의 길이 (압축기와 다른 경우)를 문자로 표시합니다.

답에 세 가지 개수와 합계를 모두 기록해야합니다. 코드 골프이기 때문에 점수가 낮을수록 좋습니다.

규칙 및 제한 사항 :

  • 당신은 사용할 수 없습니다 그들이 선택한 언어를 번들로 제공하는 경우에도, 어떤 기존의 압축과 압축 해제 도구 나 라이브러리를. 주어진 도구 또는 기능이 허용되는지 확실하지 않은 경우 문의하십시오.

  • 컴프레서 프로그램은 탭 (ASCII 9) 및 줄 바꿈 (ASCII 10)을 포함하여 인쇄 가능한 ASCII 텍스트 로 구성된 입력을 처리 할 수 ​​있어야합니다 . 임의의 유니 코드 및 / 또는 이진 입력을 처리 할 수 ​​있지만 반드시 그럴 필요는 없습니다.

  • 압축 해제 프로그램은 입력과 압축기에 제공된 것과 정확히 동일한 출력 을 생성해야합니다 . 특히 입력에 후행 줄 바꿈이없는 경우 줄 바꿈 출력을 출력하지 않도록주의하십시오. 아래 테스트 입력에는 후행 줄 바꿈이 있으므로 별도로 테스트해야합니다. GolfScript 팁 : '':n.

  • 컴프레서와 압축 해제 기는 동일한 프로그램 일 수 있습니다 (예 : 명령 모드 스위치와 같은 적절한 모드가 선택된 상태). 이 경우 길이는 한 번만 계산됩니다 .

  • 프로그램 속도너무 느리거나 메모리가 부족 해서는 안됩니다 . 새롭지 않은 새 데스크톱 (2.2GHz AMD Athlon64 X2)에서 테스트 입력을 압축 또는 압축 해제하는 데 1 분 이상 걸리거나 기가 바이트 이상의 RAM을 사용하는 경우 솔루션을 무효로 간주합니다. 이러한 제한은 의도적으로 허술한 것입니다. 밀지 마십시오. (아래 수정 사항 참조 :이 한계 내에서 최소 100kB의 입력을 처리 할 수 ​​있어야합니다.)

  • 테스트 입력 만 스코어링에 중요하지만 최소한 임의의 입력 텍스트를 압축 하는 데 노력해야 합니다. 테스트 입력에 대해서만 적절한 압축 비율을 달성하고 다른 것에 대해서는 적절한 솔루션 은 기술적으로 유효하지만지지를 얻지 못할 것입니다.

  • 컴프레서 및 압축 해제 프로그램 은 독립적이어야합니다 . 특히, 선택한 언어의 표준 런타임 환경에 포함되지 않은 일부 파일 또는 네트워크 리소스를 읽을 수있는 경우 해당 파일 또는 리소스의 길이는 프로그램 길이의 일부로 계산해야합니다. (이것은 입력을 웹의 파일과 비교하고 일치하는 경우 0 바이트를 출력하는 "압축기"를 허용하지 않기위한 것입니다. 더 이상 새로운 트릭이 아닙니다.)

수정 및 설명 :

  • 컴프레서는 합리적인 시간과 메모리 사용 (최대 1 분 1GB의 메모리) 내에 최소 100kB 의 일반 영어 텍스트 로 구성된 파일을 처리 할 수 ​​있어야합니다 . 압축 해제 기는 결과 한계를 동일한 한계 내에서 압축 해제 할 수 있어야합니다. 물론, 그보다 긴 파일을 처리 할 수있는 것은 완벽하고 훌륭합니다. 긴 입력 파일을 청크로 분할하여 개별적으로 압축하거나 다른 방법을 사용하여 긴 입력의 속도에 대한 압축 효율을 절충 할 수 있습니다.

  • 압축 해제 기가 출력에서 ​​동일한 줄 바꾸기 표현을 사용하는 한, 선호하는 플랫폼의 기본 줄 바꾸기 표현 (LF, CR + LF, CR 등)을 사용하여 압축기에 입력을 제공해야 할 수도 있습니다 . 물론, 압축 해제 기가 원래 입력에서와 동일한 종류의 개행을 출력하는 한 압축기가 모든 종류의 개행 (또는 플랫폼에 관계없이 Unix 개행 만)을 받아들이는 것도 좋습니다.

테스트 입력 :

답변의 압축 효율을 판단하려면 다음 테스트 입력 ( 레이븐 에드가 앨런 포에 의해 프로젝트 구텐베르크의 의례 )이 사용됩니다 :

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!

올바른 테스트 입력 (유닉스 스타일 LF 줄 바꿈으로 인코딩 됨)의 길이는 7043 바이트 여야하며 16 진수 MD5 해시가 있어야합니다 286206abbb7eca7b1ab69ea4b81da227. ( md5sum -tDOS / Windows에서 CR + LF 줄 바꿈을 사용하더라도 동일한 해시 값을 생성해야합니다.) 압축 해제 기의 출력 길이와 해시가 같아야합니다.

추신. 이 도전은 당신이하는 것만 큼 어렵다는 것을 명심하십시오. 실제로 7043 미만은 좋은 점수로 간주됩니다. (스케일의 다른 쪽 끝에서, 누군가가 2500 점 미만의 점수를 얻는다면 나는 매우 감동 할 것입니다.)


그래서 압축 손실 을보고 싶지 않다고 생각 합니까?
Mr. Llama

2
MD5 해시를 찾을 수없는 사람들을위한 선제 적 참고 : 텍스트 파일에는 줄 끝을위한 Unix 줄 바꿈이 있습니다. 또한 파일에 전체 7043 바이트 길이의 마지막 줄 바꿈이 있어야합니다.
Mr. Llama

@ GigaWatt : 예, 줄 바꿈에 대해 더 분명해야했습니다. 입력을 ASCII 텍스트로만 제한했기 때문에 사람들이 일관되게 사용하는 한 줄 바꿈 규칙이 가장 자연스럽게 사용되도록 할 수 있다고 생각합니다. 나는 도전에서 그것을 표현하는 좋은 방법을 생각하려고 노력할 것이다. 그리고 컴프레서는 손실되지 않아야합니다.
Ilmari Karonen

파일 길이는 예를 들어, 크기가 큰 파일 또는 훨씬 큰 파일 (> 일부 MB)에 대해서만 (허용 가능한 시간 내에) 실행해야합니까?
차례에 중단는 counterclockwis

1
출력이 압축기와 동일한 언어로 프로그램으로 제공되는 경우 압축 해제 기의 길이를 0으로 계산할 수 있습니까?
피터 테일러

답변:


19

펄, 3502 = 133 + 3269 + 100

인코더 :

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

그리고 디코더 :

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

명령 행 스위치를 사용하지 않으려는 순수 주의자 : shebang 라인을 제거 $/=chr;하고 인코더 및 $/=$,;디코더에 추가 하여 동일한 효과를 얻을 수 있습니다. (이로 인해 점수가 3510이됩니다.)

이 코드는 매우 원시적 인 압축 체계를 사용합니다.

  • 소스 텍스트에서 가장 자주 나타나는 2 자리 bigram을 찾으십시오.
  • bigram을 현재 사용하지 않는 바이트 값으로 바꾸십시오.
  • 더 이상 반복되는 bigram이 없거나 사용되지 않는 바이트 값이 없을 때까지 반복하십시오.

누군가가 이것을 "재 페어"압축 (재귀 쌍의 줄임말)의 단순화 된 버전으로 인식 할 수 있습니다.

일반적인 압축 방식은 좋지 않습니다. 사용하지 않는 바이트 값이 많은 ASCII 텍스트와 같은 것만으로도 잘 수행되며 일반적으로 45-50 %의 비율을 넘지 않습니다. 그러나 최소한의 코드로 구현할 수 있다는 장점이 있습니다. 압축 해제 기는 특히 매우 콤팩트 할 수있다 . 내 디코더 스크립트의 대부분의 문자는 bigram 사전을 검색하기위한 것입니다.

코드의 압축되지 않은 버전은 다음과 같습니다.

#!/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, $_;
}

골프 엔코더의 한 표현에는 설명이 필요 (sort{$p{$a}<=>$p{$b}}keys%p)[-1]합니다. 즉, 가장 높은 값을 가진 키를 얻으려면 이라고 생각 합니다. 로 작성 해야하는 것처럼 보이며 (sort{$p{$b}<=>$p{$a}}keys%p)[0]동일한 작업을 수행하고 한 문자가 더 짧습니다. 내가 그렇게 작성하지 않은 이유는 가장 높은 값을 가진 여러 키가있는 경우 선택한 키를 변경하기 때문입니다. 우연히 이로 인해 테스트 입력의 결과 출력이 10 바이트 더 길어졌습니다. 나는 쓸모없는 여분의 성격을 취하는 것을 싫어했지만 내 점수에서 9 점을 희생하기에는 충분하지 않습니다.

당신의 얼굴에 Golfscript! (Haha, Golfscript가 완전히 와서 내 말을들을 수 있다면 내 엉덩이를 차버릴 것입니다.)


3
와, 정말 인상적입니다! 추신. 이것은 커맨드 라인 스위치의 카운트와 관련하여 일반적으로 인정되는 답변 인 것 같습니다.
Ilmari Karonen

Dang, 나는 그것을 일찍 읽었지만 중간에 그 비트를 알아 차리지 못했습니다. -e코드에 작은 따옴표 문자가 포함되어 있지 않은 경우 코드에 작은 따옴표 문자가 포함되어 있지 않으면 초기 하이픈 문자를 계산하지 않습니다 ( 옵션 번들에 추가 할 수 있기 때문에). 이제 명령 줄에서 작은 따옴표를 이스케이프 처리하지 않으려면 shebang 줄이있는 파일에서 파일을 실행해야합니다).
breadbox

1
이 기술을 바이트 쌍 인코딩 이라고도 합니다.
훌륭한

@roblogic 참조 해 주셔서 감사합니다. 알아두면 좋습니다.
breadbox

20

파이썬, 3514 = 294 + 2894 + 326

기본적으로 bzip2 구현입니다. 그것은 비트 스트림으로의 간단한 허프만 인코딩 , Burrows-Wheeler 변환 , 전면 이동 , 변환을 수행하여 비트 스트림을 정수로 변환하고 바이트를 씁니다.

인코더 :

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

S프론트-투-프레미스 큐, H허프만 인코더, N비트 스트림입니다.

인코딩은 테스트 입력을 원래 크기의 약 41 %로 줄입니다.

디코더 :

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
나는 BWT를 구현하고 진정한 압축 형태를 취하고 싶지만 너무 게으르다. : P
Mr. Llama

8

8086 어셈블러 / MS_DOS

압축기 : 155

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

데이터 : 3506

압축 해제 기 : 203

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

합계 : 3864

사용 이 Base64로 디코더 와 'compress.com'와 'decompress.com'로 바이너리 파일을 저장하고 수행

compress < source > compressed_file
decompress < compressed_file > copy_of_source

DOS 쉘에서 (WinXP로 테스트). 오류 검사가 없으므로 큰 파일을 압축하면 잘못된 결과가 생성됩니다. 몇 가지 작은 추가 사항이 있으며 모든 크기의 파일에 대처할 수 있습니다. 또한 0xff 값을 출력 할 수 없으므로 바이너리로 압축을 풀 수 없습니다 (압축 된 데이터는 0xff 값을 0xfe 0xff로 이스케이프하고 0xfe는 0xfe 0xfe로 이스케이프 처리). 명령 줄 파일 이름을 사용하면 이진 출력 문제를 극복 할 수 있지만 더 큰 실행 파일이됩니다.


프로그램은 어떤 종류의 압축 알고리즘을 사용합니까?
Sir_Lagsalot

@Sir_Lagsalot : 가변 비트 폭 LZW (GIF 파일에서 사용되는)를 사용합니다.
Skizz

6

배쉬시 (566 + 117) + 4687 = 5370

재미로 나는 압축기를 시로 위장했습니다.

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

이것은 통합 된 압축기입니다. 옵션 "c"로 실행하면 압축되고 "d"로 압축 해제됩니다. 그것은 566 바이트 "독자 다이제스트"버전의시와 (2) 모든 "실제"배쉬가 수행되는 117 바이트 접미사로 구성됩니다.

주의를 기울이면 (예 : "I in"으로시 시작) bash는 "손실"버전의시를 배열로 해석합니다. 배열의 각 요소를 ASCII가 아닌 문자로 바꿉니다 (우리는 입력이 ASCII라고 가정하므로 충돌이 없습니다). 이 솔루션의 한 가지 작은 장점 : 입력이 ASCII라고 가정 할 수 있기 때문에 입력 및 / 또는 손실 부분이 무엇이든이 압축의 출력은 입력보다 길지 않습니다.

위반에 가장 가까운 규칙은 다른 텍스트에 적절한 압축 비율을 제공하는 규칙입니다. 그러나 GPL V2 텍스트에서 1386 바이트를 자체 크기보다 크게 잘라냅니다 decent. 의 OP 정의와 일치하는 것 같습니다 . 따라서 decent일반 텍스트에 대해 소위 압축 을 제공하는 것 같습니다 . 이것은 거의 모든 영어 텍스트에 "the" "that"등이 있기 때문입니다. "손실"부분을 무손실 압축하려는 원본과 유사한 텍스트로 바꾸면 분명히 더 잘 작동합니다.

사진과 오디오를 손실 부분과 비 손실 부분으로 나누는 것은 알려진 기술입니다. 텍스트의 경우 잘 작동하지 않습니다. 손실 버전에서 566 바이트를 제외하더라도 4687 바이트는 그다지 좋지 않으며 오디오와 동일한 방식으로 손실 텍스트 버전을 자동으로 생성 할 수 없습니다. 플러스 측면에서 이것은이 압축기로 무언가를 압축 할 때마다 손으로 손실 버전을 만드는 재미를 가질 수 있음을 의미합니다. 따라서 이것은 합리적인 "재미있는"솔루션처럼 보입니다.


5

C ++, 4134 바이트 (코드 = 1357, 압축 = 2777)

이것은 Burrows-Wheeler 변환 + Keith Randall과 같은 Move-To-Front를 수행하지만 적응 형 Range Coder를 사용하여 결과 바이트 시퀀스를 압축합니다 . 불행히도, 레인지 코더의 향상된 압축만으로는 C ++의 세부 정보를 상쇄하기에 충분하지 않습니다. 이 코드를 좀 더 골라 볼 수 있습니다. 즉, 다른 입력 / 출력 방법을 사용하지만 현재 알고리즘으로 다른 제출물을이기는 것만으로는 충분하지 않습니다. 이 코드는 Windows 전용이며 ASCII 텍스트 만 지원됩니다.
압축
을 풀려면 : "C text_file compress_file"압축 을 풀려면 : "D compress_file uncompressed_file"
거의 모든 명령 행 오류 또는 파일 오류로 인해 프로그램이 중단되며시를 인코딩하거나 디코딩하는 데 1 분이 더 걸립니다.

#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

자바 스크립트, 393 (코드) + 3521 (테스트) = 3914 (전체)

이 프로그램은 입력의 2-4 문자 청크에 대해 사용되지 않는 바이트 값을 반복적으로 대체합니다. 각 교체는 원래 청크의 빈도와 길이에 따라 점수가 매겨지며 매번 가장 좋은 교체가 선택됩니다. 비교적 적은 수의 문자로 수행하는 방법을 알 수 있다면 최종 허프만 코딩 단계를 추가 할 것입니다. 감압은 기본적으로 일련의 찾기 및 바꾸기 작업입니다.

용법

C ()는 압축을 제공합니다. U ()는 감압을 제공합니다. JavaScript의 문자열은 16 비트 유니 코드 코드 단위를 기반으로하기 때문에 각 코드 단위의 최하위 8 비트 만 압축 된 데이터 형식으로 사용됩니다. 이것은 Base64 인코딩을위한 Firefox의 btoa () 및 atob () 함수와 호환됩니다. ( 사용 예 )

이 프로그램은 .replace ()에 대한 비표준 "g"옵션으로 인해 Firefox에서만 작동 할 수 있습니다.

암호

골프 코드 :

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}

골프 전 :

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

내가 왜 C(text).length=7301? (FF 60.0.2)
l4m2

3

PHP, (347 + 6166 + 176) = 6689

그래서 나는 단순한 사전 + 대체 접근법을 사용했습니다.

단어가 여러 번 나타나고 더 짧은 경우 (단어를 인코딩 + 대체 항목 저장), 대체합니다. "단어"가 숫자이면 어쨌든 압축 해제 중에 실수로 대체되는 것을 방지하기 위해 수행됩니다. 대체의 "사전"은 널 바이트, 두 개의 널 바이트, 대체가 작동하는 본문으로 연결됩니다.

가능한 개선 사항 :

  • Windows는 4kb 이상의 데이터를 파이핑하는 것을 좋아하지 않으므로 파일을 사용하는 것보다 더 나은 방법을 찾으십시오.
  • 긴 공백 문자열을 일치시키고 너무 많은 코드를 추가하지 않고 "단어"로 계산하는 기능.
  • 숫자를 사용하는 대신 더 나은 대체물을 생각해냅니다.

사용법 : 압축기는 "i"라는 파일을 찾고 압축 된 데이터를 "o"에 씁니다. 압축 해제 기는 "o"를 찾고 압축되지 않은 데이터를 "d"에 씁니다. 이것은 데이터 보트를 파이프하기를 좋아하지 않는 Windows에 대한 저의 임시 해결책입니다.


compress.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);

주석과 설명이 포함 된 확장 버전 .


사전없이 출력 샘플 . 보고 우스운.
보통 크기 : 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."

decompress.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);

설명이있는 확장 버전 .


개선을위한 제안은 환영합니다.

편집 : 코드의 "롤링되지 않은"버전을 추가하고 많은 주석을 추가했습니다. 따라하기 쉬워야합니다.


가! 내가 사용했던 것과 같은 언어와 방법! 젠장. 한 마디 만 건너 뛰는 것은 아니지만
Gareth

본문에 숫자가 있으면 어떻게 되나요? 결국 원래 숫자를 다른 단어로 바꿉니다. 비슷한 접근법 (정규 분할, 대체 사전을 대체하고 대체 사전을 생성하고 null로 붙이는)을 사용했지만 숫자 대신 유니 코드 문자를 사용했습니다 (chr (128)부터 시작).
Blazer

@Blazer : 실제로 ||is_int($w)사전에 숫자를 추가하여 숫자를 처리하는 코드 (예 :)가 있지만, 버그가있는 것 같습니다. 구텐베르크 전자 텍스트 전체를 압축하고 압축을 풀면 출력은로 시작합니다 The 4 3 EBook 2 The Raven, by Edgar Allan Poe. :-( 문제는 무언가가 두 번 교체되고 있다고 생각합니다; strtr()대신이 문제를 피하기 위해 대신 사용하는 것이 좋습니다.
Ilmari Karonen

@Ilmari 숫자가 많은 문서가있는 경우 사전에 해당 숫자를 추가하면 압축이 원래보다 커질 수 있습니다. 1-2 자 길이의 여러 항목을 저장하는 것은 효과적이지 않습니다. 문서에서 'a'라는 단어를 바꾸는 것처럼
Blazer

@Blazer-모든 압축 알고리즘에 대해 더 큰 출력을 초래하는 특정 입력이 있습니다 . 엔트로피 데이터를 안정적으로 압축 할 수없는 것과 마찬가지로 무손실 압축에 내재되어 있습니다.
Mr. Llama

3

GolfScript, 3647 (압축 크기 3408 + 코드 크기 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

사용 된 알고리즘은 가변 폭 코드를 사용한 LZW 압축입니다. 첫 번째 줄은 공유 코드이고 두 번째 줄은 압축 코드이고 세 번째 줄은 압축 해제 코드입니다.

ASCII 문자가 1-127 범위 인 파일을 처리하고 압축 파일을 자동으로 인식하므로 (0 바이트로 시작) 압축 해제에 필요한 매개 변수가 없습니다.

예제 실행 :

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

참고 : 100kb 처리 요구 사항이 추가되기 오래 전에이 작업을 시작 했으므로 해당 크기의 입력에서 테스트하지 않았습니다. 그러나 최대 20MB의 메모리를 사용하여 테스트 입력을 압축하는 데 약 30 초, 압축 해제하는 데 5 초가 걸립니다.


76 kB의 파일을 압축하면 그것은 10 소요 압축 해제하는 동안 19 분 정도 걸릴 것 입니다 그래서 ... 나는 몰라, 종류 느린의, 그러나 다시 원래 규칙을 통과하지합니다. 상황에서 허용하지 않는 것이 불공평 한 것 같습니다. 나는 당신이나 무언가를 위해 암시적인 "할아버지 조항"을 부를 수 있다고 생각합니다.
Ilmari Karonen

3

하스켈, 3973

파티에 늦게 이기지 않았지만 재미있게 작성하여 게시 할 수있었습니다.

인쇄 가능한 ASCII, 탭 및 줄 바꿈으로 사전이 명시 적으로 제한된 LZW의 간단한 가변 너비 구현입니다. 인수없이 실행하면 표준 입력을 file로 압축합니다 C. 인수로 실행하지만 "-압축 해제"는 합당한 방법 C입니다. 파일 을 표준 출력으로 압축 해제합니다 .

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)
  • 코드 크기 : 578
  • 압축 샘플 크기 : 3395
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.