Compactar dados com gramáticas livres de contexto

9

É possível compactar alguns tipos de dados, como texto humano ou código fonte, com gramáticas lineares. Você basicamente cria uma gramática cujo idioma possui exatamente uma palavra - os dados não compactados. Nesta tarefa, você precisa escrever um programa que implemente esse método de compaixão de dados.

Entrada

A entrada é uma cadeia de caracteres com comprimento não superior a 65535 bytes. É garantido que a entrada corresponda à expressão regular [!-~]+(ou seja, pelo menos um caractere ASCII imprimível, excluindo espaços em branco).

Um exemplo de entrada é

abcabcbcbcabcacacabcabab

Resultado

A saída é um conjunto de regras que formam uma gramática que descreve exatamente uma palavra (a entrada). Cada não-terminal é indicado por um número decimal maior que 9. O símbolo inicial é o número dez. Um exemplo de saída correspondente à entrada de exemplo é dado abaixo; sua sintaxe é descrita mais abaixo:

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

Cada regra tem o formato <nonterminal>=<symbol> <symbol> ...com um número arbitrário de símbolos separados por espaços em branco no lado direito. Cada saída que obedece às seguintes restrições e deriva exatamente a sequência de entrada é válida.

Restrições

Para impedir que as pessoas façam coisas estranhas, várias restrições estão ocorrendo:

  • Cada não-terminal deve aparecer pelo menos duas vezes no lado direito de uma regra. Por exemplo, a seguinte gramática para a entrada abcabcé ilegal porque a regra 12 aparece apenas uma vez:

    10=12
    11=a b c
    12=11 11
    
  • Nenhuma sequência de dois símbolos adjacentes pode aparecer mais de uma vez no lado direito de todas as regras, exceto se elas se sobrepuserem. Por exemplo, a seguinte gramática para a entrada abcabcbcé ilegal, pois a sequência bcaparece duas vezes:

    10=11 11 b c
    11=a b c
    

    Uma gramática válida seria:

    10=11 11 12
    11=a 12
    12=b c
    
  • Seu programa deve terminar em menos de um minuto para cada entrada válida que não exceda 65535 bytes.

  • Como de costume, você não pode usar nenhum recurso do seu idioma ou qualquer função de biblioteca que torne a solução trivial ou implemente grande parte dela.

Entrada de amostra

Gere entrada de amostra com o seguinte programa 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;
}

A entrada de amostra gerada pelo programa acima geralmente não resultará em bons resultados de compactação. Considere usar texto humano ou código-fonte como exemplo de entrada.

Critérios de vitória

Isso é código de golfe; o programa com o menor código fonte vence. Para crédito extra, escreva um programa que reconstrua a entrada da saída.

FUZxxl
fonte
Ha ha. Já tenho algumas versões deste implementada (mas não golfed) em Java para os Kolmogorov-complexidade perguntas ...
Peter Taylor
@PeterTaylor Que perguntas exatamente?
FUZxxl
Ele não necessariamente encontra uma resposta curta o suficiente para valer a pena postar (estou adicionando estratégias de geração de gramática e mecanismos de gramática lentamente), mas o script principal desse projeto os experimenta em codegolf.stackexchange.com/questions/1682 , codegolf .stackexchange.com / questions / 6043 , codegolf.stackexchange.com/questions/4191 , codegolf.stackexchange.com/questions/4356 e alguns componentes de outras perguntas.
31412 Peter

Respostas:

3

GolfScript, 111 108 caracteres

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

Essa é uma abordagem bastante desajeitada usando o GolfScript. A segunda versão tem um desempenho muito melhor que a inicial. É muito mais longo que o código pretendido, mas minha implementação aninhava do-loops e isso causava problemas com o intérprete.

Exemplos:

> 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
Howard
fonte
1

Retraído - o algoritmo não pode lidar com todos os casos. C, 422 (corrigido para remover dups na saída e caracteres eliminados)

Implementação inicial, começará a jogar.

Como as regras não exigem que ele realmente comprima essa abordagem ingênua de força bruta, isso servirá ...

Pode processar 65535 de comprimento em 10 segundos

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

}

Exemplo de execução:

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

coelho bebê
fonte
Seu código não funciona de acordo com a especificação. Ele gera uma saída que viola a regra. Nenhuma sequência de dois caracteres pode aparecer duas vezes ; considere a entrada abcdabcd. Além disso, seu código aparentemente remove o último byte do fluxo de entrada. Veja aqui um exemplo para observar os dois efeitos: ideone.com/3Xvtyv
FUZxxl
Além disso, sua saída de amostra está aparentemente errada.
FUZxxl
Você está certo - eu falhei - vou dar uma olhada quando voltar do trabalho: P
baby-rabbit
Não está removendo o último byte da entrada para mim - e minha saída de amostra está correta (para mim). Vamos jogar "localize o bug"!
baby-coelho
A saída de amostra que você postou definitivamente é. A forma expandida da regra 10 termina com a regra 14, que por sua vez termina com "ca". O último c é na verdade 5 posições antes do final.
FUZxxl