Comprimi i dati con grammatiche libere dal contesto


9

È possibile comprimere alcuni tipi di dati, come testo umano o codice sorgente, con grammatiche lineari. Fondamentalmente si crea una grammatica la cui lingua ha esattamente una parola: i dati non compressi. In questa attività, è necessario scrivere un programma che implementa questo metodo di compressione dei dati.

Ingresso

L'input è una stringa di lunghezza non superiore a 65535 byte. È garantito che l'input corrisponde all'espressione regolare [!-~]+(ovvero almeno un carattere ASCII stampabile escluso lo spazio bianco).

Un esempio di input è

abcabcbcbcabcacacabcabab

Produzione

L'output è un insieme di regole che formano una grammatica che descrive esattamente una parola (input). Ciascun nonterminale è indicato da un numero decimale maggiore di 9. Il simbolo iniziale è il simbolo numero dieci. Di seguito viene fornito un output di esempio corrispondente all'input di esempio; la sua sintassi è descritta più avanti:

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

Ogni regola ha la forma <nonterminal>=<symbol> <symbol> ...con un numero arbitrario di simboli separati da spazi bianchi sul lato destro. Ogni output che rispetta le seguenti restrizioni e deriva esattamente la stringa di input è valido.

restrizioni

Per impedire alle persone di fare cose strane, si stanno verificando una serie di restrizioni:

  • Ogni nonterminale deve apparire almeno due volte sul lato destro di una regola. Ad esempio, la seguente grammatica per l'input abcabcè illegale perché la regola 12 appare una sola volta:

    10=12
    11=a b c
    12=11 11
    
  • Nessuna sequenza di due simboli adiacenti può apparire più di una volta in tutti i lati di destra di tutte le regole, tranne se si sovrappongono. Ad esempio, la seguente grammatica per l'input abcabcbcè illegale poiché la sequenza bcappare due volte:

    10=11 11 b c
    11=a b c
    

    Una grammatica valida sarebbe:

    10=11 11 12
    11=a 12
    12=b c
    
  • Il programma deve terminare in meno di un minuto per ogni input valido che non superi i 65535 byte.

  • Come al solito, non è possibile utilizzare alcuna funzionalità della propria lingua o alcuna funzione di libreria che renda banale la soluzione o ne implementi gran parte.

Input di esempio

Generare input di esempio con il seguente programma 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;
}

L'input di esempio generato dal programma sopra riportato di solito non porterà a buoni risultati di compressione. Prendi in considerazione l'utilizzo di testo umano o codice sorgente come input di esempio.

Criteri vincenti

Questo è il codice golf; vince il programma con il codice sorgente più breve. Per ulteriore credito, scrivere un programma che ricostruisce l'input dall'output.


Ah ah Ne ho già alcune versioni implementate (ma non giocate a golf) in Java per le domande sulla complessità del kolmogorov ...
Peter Taylor,

@PeterTaylor Quali domande esattamente?
FUZxxl,

Non trova necessariamente una risposta abbastanza breve per cui valga la pena pubblicare (sto aggiungendo lentamente strategie di generazione della grammatica e motori grammaticali), ma lo script principale in quel progetto li prova su codegolf.stackexchange.com/questions/1682 , codegolf .stackexchange.com / questions / 6043 , codegolf.stackexchange.com/questions/4191 , codegolf.stackexchange.com/questions/4356 e alcuni componenti di altre domande.
Peter Taylor,

Risposte:


3

GolfScript, 111 108 caratteri

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

Questo è un approccio piuttosto maldestro con GolfScript. La seconda versione funziona molto meglio di quella iniziale. È molto più lungo del codice previsto, ma la mia implementazione aveva cicli do nidificati e questo ha causato problemi con l'interprete.

Esempi:

> 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

Ritirato: l'algoritmo non può gestire tutti i casi. C, 422 (risolto per rimuovere i duplicati nell'output e carattere rilasciato)

L'implementazione iniziale, inizierà a giocare a golf.

Dal momento che le regole non lo richiedono per comprimere effettivamente questo approccio ingenuo forza bruta farà ...

Può elaborare una lunghezza di 65535 entro 10 secondi

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

}

Esecuzione di esempio:

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


Il tuo codice non funziona secondo le specifiche. Genera output che viola la regola che nessuna sequenza di due caratteri può apparire due volte ; considerare l'ingresso abcdabcd. Inoltre, il tuo codice apparentemente rimuove l'ultimo byte dal flusso di input. Guarda qui un esempio per osservare entrambi gli effetti: ideone.com/3Xvtyv
FUZxxl

Inoltre, l'output di esempio è apparentemente errato.
FUZxxl,

Hai ragione - ho fallito - lo guarderò quando torno dal lavoro: P
baby-rabbit

Non rimuove l'ultimo byte dall'input per me - e il mio output di esempio è corretto (per me) .. Giochiamo a "spot the bug"!
baby-rabbit,

L'output di esempio che hai pubblicato è definitivamente. La forma espansa della regola 10 termina con la regola 14 che a sua volta termina con "ca". L'ultima c è in realtà 5 posizioni prima della fine.
FUZxxl,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.