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


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.