Сжать данные с помощью контекстно-свободных грамматик


9

Можно сжимать некоторые виды данных, такие как человеческий текст или исходный код, с помощью линейных грамматик. Вы в основном создаете грамматику, язык которой содержит ровно одно слово - несжатые данные. В этой задаче вы должны написать программу, которая реализует этот метод сжатия данных.

вход

Входные данные представляют собой строку длиной не более 65535 байтов. Гарантируется, что входные данные соответствуют регулярному выражению [!-~]+(т. Е. Хотя бы одному печатному символу ASCII, исключая пробел).

Пример ввода

abcabcbcbcabcacacabcabab

Вывод

Выходные данные представляют собой набор правил, которые образуют грамматику, которая описывает ровно одно слово (входные данные). Каждый нетерминал обозначается десятичным числом больше 9. Начальный символ - символ номер десять. Пример вывода, соответствующий примеру ввода, приведен ниже; его синтаксис описан ниже:

10=11 11 12 12 11 13 13 11 14 14
11=a 12
12=b c
13=a c
14=a b

Каждое правило имеет форму <nonterminal>=<symbol> <symbol> ...с произвольным количеством символов, разделенных пробелами, с правой стороны. Каждый вывод, который подчиняется следующим ограничениям и получает именно входную строку, является действительным.

ограничения

Для того, чтобы люди не могли совершать странные поступки, существует ряд ограничений:

  • Каждый нетерминал должен появляться как минимум дважды с правой стороны правила. Например, следующая грамматика для ввода abcabcнедопустима, потому что правило 12 появляется только один раз:

    10=12
    11=a b c
    12=11 11
    
  • Никакая последовательность из двух соседних символов не может появляться более одного раза во всех правых частях всех правил, кроме случаев, когда они перекрываются. Например, следующая грамматика для ввода abcabcbcнедопустима, так как последовательность bcпоявляется дважды:

    10=11 11 b c
    11=a b c
    

    Действительная грамматика будет:

    10=11 11 12
    11=a 12
    12=b c
    
  • Ваша программа должна завершиться менее чем за одну минуту для каждого допустимого ввода, который не длиннее 65535 байт.

  • Как обычно, вы не можете использовать какие-либо средства вашего языка или библиотечные функции, которые делают решение тривиальным или реализуют его большую часть.

Пример ввода

Сгенерируйте ввод образца с помощью следующей программы на языке C.

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv) {
  unsigned int i,j = 0,k;

  if (argc != 3
     || 2 != sscanf(argv[1],"%u",&i)
      + sscanf(argv[2],"%u",&k)) {
    fprintf(stderr,"Usage: %s seed length\n",argv[0]);
    return EXIT_FAILURE;
  }

  srand(i);

  while(j < k) {
    i = rand() & 0x7f;
    if (i > 34 && i != 127) j++, putchar(i);
  }

  return EXIT_SUCCESS;
}

Пример ввода, сгенерированный программой выше, обычно не приводит к хорошим результатам сжатия. Рассмотрите возможность использования человеческого текста или исходного кода в качестве примера ввода.

Критерии победы

Это код гольф; программа с самым коротким исходным кодом выигрывает. Для дополнительного кредита напишите программу, которая восстанавливает входные данные из выходных данных.


Ха ха У меня уже есть несколько версий этого, реализованных (но не в гольф) на Java для вопросов колмогоровской сложности ...
Питер Тейлор

@PeterTaylor Какие именно вопросы?
FUZxxl

Не обязательно найти достаточно короткий ответ, который стоит опубликовать (я медленно добавляю стратегии генерации грамматики и механизмы грамматики), но основной скрипт в этом проекте проверяет их на codegolf.stackexchange.com/questions/1682 , codegolf .stackexchange.com / questions / 6043 , codegolf.stackexchange.com/questions/4191 , codegolf.stackexchange.com/questions/4356 и несколько компонентов других вопросов.
Питер Тейлор

Ответы:


3

GolfScript, 111 108 символов

1/{.}{:^1<{^1$/,2>.{;,)^<.0?)!}*}do-1<.,1>{^1$/[10):10]*0+\+}{;^}if(\}while][0]%.,,]zip{))9+`"="+\~" "*+}%n*

Это довольно неуклюжий подход с использованием GolfScript. Вторая версия работает намного лучше, чем начальная. Это намного длиннее, чем предполагалось, но моя реализация имела вложенные циклы do, и это вызывало проблемы с интерпретатором.

Примеры:

> abcba
10=a b c b a

> abcabcbc
10=11 11 12
11=a 12
12=b c

> abcabcbcbcabcacacabcabab
10=11 12 12 13 14 14 c 11 15
11=15 13
12=c b
13=14 b
14=c a
15=a b

1

Убрано - алгоритм не может обрабатывать все случаи. C, 422 (исправлено, чтобы убрать дубликаты на выходе и пропущенный символ)

Начальная реализация, начнется игра в гольф.

Так как правила не требуют, чтобы это фактически сжимало этот грубый наивный подход, подойдет ...

Может обработать длину 65535 в течение 10 секунд

n,m[99999];
c,r[99999][2];

g,i,s,t;

main(){
    for(;(m[n]=getchar())>32;n++);

    while(!g){ // loop until no further changes
        g=1;
        for(s=0;s<n-1;s++) {
            for(t=s+2;t<n-1;t++)if(m[s]==m[t]&&m[s+1]==m[t+1]){
                // create rule
                r[c][0]=m[s];
                r[c++][1]=m[s+1];
                g=0;
                // substitute
                for(i=t=s;i<n;i++){
                    if(m[i]==r[c-1][0]&&m[i+1]==r[c-1][1]){
                        m[t++]=-c;
                        i++;
                    }else
                        m[t++]=m[i];
                }
                n=t;
            }
        }
    }

    for(s=-1;s<c;s++){
        printf("%d=",s+11);
        for(t=0;t<(s<0?n:2);t++){
            i=(s<0?m:r[s])[t];
            i<0?printf("%d ",10-i):printf("%c ",i);
        }
        printf("\n");
    }

}

Образец прогона:

echo abcabcbcbcabcacacabcabab | a.out
10=11 12 13 13 12 14 14 12 12 11 
11=a b 
12=c 11 
13=c b 
14=c a


Ваш код не работает в соответствии со спецификацией. Он генерирует вывод, который нарушает правило, никакая последовательность из двух символов не может появляться дважды ; рассмотрим ввод abcdabcd. Кроме того, ваш код, очевидно, удаляет последний байт из входного потока. Посмотрите здесь пример для наблюдения обоих эффектов: ideone.com/3Xvtyv
FUZxxl

Кроме того, ваш пример вывода, по-видимому, неправильно.
FUZxxl

Вы правы - я потерпел неудачу - я посмотрю на это, когда вернусь с работы: P
Кролик

Это не удаление последнего байта из входных данных для меня - и мой пример выходных данных является правильным (для меня) .. Давайте играть "выявить ошибку"!
Кролик

Пример вывода, который вы опубликовали, определенно есть. Расширенная форма правила 10 заканчивается правилом 14, которое, в свою очередь, оканчивается на «ca». Последний c на самом деле 5 позиций до конца.
FUZxxl
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.