Проблема стиснення тексту англійською мовою без втрат [закрито]


12

Виклик:

Ваше завдання (якщо ви вирішите прийняти це) - стиснути та розпакувати 5 МБ " Повних творів Вільяма Шекспіра ", як їх можна знайти тут: http://www.gutenberg.org/cache/epub/100/pg100.txt

(MD5: a810f89e9f8e213aebd06b9f8c5157d8)

Правила:

  • Ви повинні приймати вхід через STDINта вихід через STDOUT...
  • ... і ви повинні надати вхід ідентичний декомпресований результат.
    • (Це означає, що ви повинні вміти cat inpt.txt | ./cmprss | ./dcmpress | md5і отримувати той самий MD5, що і вище.)
    • (Все, що STDERRє через , слід викинути.)
  • Ви повинні використовувати менш 2048 символів для вашого загального вихідного коду.
    • (Це НЕ код-гольф. Ви НЕ будучи забив на основі довжини вихідного коду. Це є просто правилом , щоб тримати речі звичайно.)
    • (Візьміть об'єднану довжину всього вихідного коду, якщо ви розділили його.)
  • Ви також повинні мати можливість (теоретично) обробляти подібні введення простого тексту.
    • (Наприклад , жорстке кодування механізму , який тільки здатний виводити наданий Шекспіра введення є неприйнятним.)
    • (Стислий розмір інших документів не має значення - якщо декомпресований результат ідентичний альтернативному вводу.)
  • Ви можете використовувати будь-який вибір мов.
    • (наприклад, не соромтесь стискати, використовуючи awkта розпаковуючи, використовуючи java)
  • Ви можете написати дві окремі програми або комбінувати їх з якоюсь формою «перемикання», як завгодно.
    • (Повинні бути чіткі демонстрації того, як викликати і режим стиснення, і декомпресію)
  • Ви не можете використовувати жодні зовнішні команди (наприклад, через exec()).
    • (Якщо ви використовуєте мову оболонки - вибачте. Вам доведеться робити вбудовані модулі. Запропонуйте опублікувати "неприйнятну" відповідь заради обміну та насолоди - але це не буде суджено! )
  • Ви не можете використовувати будь-які вбудовані або надані бібліотекою функції, для яких вказана мета - стиснути дані (наприклад gz, тощо)
    • (Зміна кодування в цьому контексті не вважається стисненням. Тут може бути застосоване певне розсуд. Сміливо аргументуйте прийнятність вашого рішення у поданні.)
  • Спробуйте розважитися, якщо вирішите взяти участь!

Всі хороші змагання мають об'єктивне визначення перемоги; ерго:

  • За умови дотримання всіх правил виграє найменший стислий результат (у STDOUTбайтах).
    • (Повідомте про результати, будь ласка, через ./cmprss | wc -c)
  • У разі розіграшу (однакові розміри виходу) виграє найбільше голосів громади.
  • У разі другого розіграшу (однакові голосування громади) я виберу переможця на основі повністю суб’єктивної перевірки елегантності та чистої геніальності. ;-)

Як подати:

Будь ласка, відформатуйте свій запис за допомогою цього шаблону:

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

<description>  (Detail is encouraged!)

    <CODE...
    ...>

<run instructions>

Я б закликав читачів та дописувачів поспілкуватися через коментарі - я вважаю, що реальна можливість для людей навчитися та стати кращими програмістами через codegolf.stack.

Перемога:

Я скоро відпущусь у відпустку: я можу (чи не можу) відстежувати подання протягом наступних декількох тижнів і закінчуватимуть виклик 19 вересня. Я сподіваюся, що це дає хороші можливості людям думати і подавати - і позитивно ділитися техніками та ідеями.

Якщо ви дізналися щось нове від участі (як читача чи дописувача), залиште коментар із заохоченням.


1
Ви повинні позначити це code-challenge.
kirbyfan64sos

1
Чи дозволено приймати введення як аргумент функції? Наприклад, рішення з мов, таких як JavaScript, не можна запустити з командного рядка, AFAIK. У моєму випадку просто запустити в браузері було б просто простіше.
ETHproductions

1
Чому шаблон? Ви збираєтеся створити фрагмент стека, який залежить від нього?
Пітер Тейлор

2
Якщо немає обмеження щодо розміру коду, що не дозволяє мені написати програму стиснення, яка друкує 0 байтів, і програму для розпакування, яка жорстко кодується для друку всіх творів Шекспіра?
Лінн

4
Можна додати правило, яке говорить про те, що код теоретично повинен працювати з іншими входами, що вирішує проблему @Mauris.
kirbyfan64sos

Відповіді:


5

Перл 5, 3651284

Просто проста словесна схема словника. Аналізує частоту слів корпусу та використовує це для визначення того, чи слід використовувати один чи два байти накладних витрат на кожне слово. Використовує два спеціальні символи для байтів \ 0 і \ 1, оскільки вони не відображаються в корпусі. Є багато інших символів, які можна було б використати. Цього не було зроблено. Не робить кодування хаффмана чи іншого джазу.

Сценарій стиснення 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;
    }
}

Сценарій декомпресії 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;
    }
}

Запустити за допомогою:

perl shakespeare.pl < pg100.txt >pg100.txt.compressed
perl deshakespeare.pl <pg100.txt.compressed >pg100.txt.restored
diff pg100.txt pg100.txt.restored
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.