Стиснення даних за допомогою контекстних граматик


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.