Contrastare la compressione LZMA2


11

Obbiettivo

Crea un programma o una coppia di programmi che interrompano collettivamente e riparino i file con l'intento di impedire a LZMA2 di funzionare in modo efficace. Le routine di interruzione e correzione devono essere reciproche, quindi è possibile ripristinare esattamente il file originale.

obiettivi

Metodi di compressione

  • Ubuntu / CORRELATI: xz -kz5 <infile>
  • Finestre: 7z.exe a -txz -mx5 <outfile> <infile>
  • Altro: utilizzare un compressore LZMA2 con livello di compressione 5 che comprime le opere di Shakespeare a 1570550 byte ± 100 byte.

scoring; somma di (tutto è in byte ls -lo diresso):

  • Dimensione del (i) programma (i). Qualunque cosa sia necessaria collettivamente per "rompere" / correggere il file in modo reversibile
  • Differenza di dimensione (assoluta) tra:
    • Lavori grezzi raccolti di Shakespeare e copia modificata (non compressa).
    • Foto non elaborata e copia modificata (non compressa).
  • Differenza di dimensioni o 0, a seconda di quale sia maggiore tra:
    • Opere raccolte a crudo di Shakespeare meno la tua copia compressa LZMA2 modificata.
    • Foto non elaborata meno la copia compressa LZMA2 modificata.

Esempio

Esempio di Python 2.x con scarso punteggio, pigramente giocato a golf, ma conforme:

import sys
x = 7919 if sys.argv[1] == 'b' else -7919
i = bytearray(open(sys.argv[2], 'rb').read())
for n in range(len(i)):
    i[n] = (i[n] + x*n) % 256
o = open(sys.argv[2]+'~', 'wb').write(i)

In esecuzione...

$ python break.py b pg100.txt 
$ python break.py f pg100.txt~ 
$ diff -s pg100.txt pg100.txt~~
Files pg100.txt and pg100.txt~~ are identical
$ python break.py b Glühwendel_brennt_durch.jpg 
$ python break.py f Glühwendel_brennt_durch.jpg~
$ diff -s Glühwendel_brennt_durch.jpg Glühwendel_brennt_durch.jpg~~
Files Glühwendel_brennt_durch.jpg and Glühwendel_brennt_durch.jpg~~ are identical
$ xz -kz5 pg100.txt~
$ xz -kz5 Glühwendel_brennt_durch.jpg~
$ ls -ln
-rw-rw-r-- 1 2092 2092     194 May 23 17:37 break.py
-rw-rw-r-- 1 2092 2092 1659874 May 23 16:20 Glühwendel_brennt_durch.jpg
-rw-rw-r-- 1 2092 2092 1659874 May 23 17:39 Glühwendel_brennt_durch.jpg~
-rw-rw-r-- 1 2092 2092 1659874 May 23 17:39 Glühwendel_brennt_durch.jpg~~
-rw-rw-r-- 1 2092 2092 1646556 May 23 17:39 Glühwendel_brennt_durch.jpg~.xz
-rw-rw-r-- 1 2092 2092 5589891 May 23 17:24 pg100.txt
-rw-rw-r-- 1 2092 2092 5589891 May 23 17:39 pg100.txt~
-rw-rw-r-- 1 2092 2092 5589891 May 23 17:39 pg100.txt~~
-rw-rw-r-- 1 2092 2092 3014136 May 23 17:39 pg100.txt~.xz

Punto

  • = 194 + abs (5589891 - 5589891) + max (5589891 - 3014136, 0) + abs (1659874 - 1659874) + max (1659874 - 1646556, 0)
  • = 194 + 0 + 2575755 + 0 + 13318
  • 2.589.267 byte. Cattivo, ma non fare nulla ai file produce un punteggio di 4.635.153 byte.

Una precisazione

Questo è il golf, quindi stai cercando di ridurre al minimo il tuo punteggio. Non sono sicuro se i commenti indicano un buco legittimo nel mio punteggio o se lo sono perché l'ho reso troppo complicato. In ogni caso, vuoi il PIÙ PICCOLO :

  • codice sorgente
  • differenza tra il file modificato non compresso e il file originale (ad es. se lo si modifica aggiungendo un trilione di 0 alla fine, il punteggio è salito di un trilione di byte)
  • differenza tra il file modificato compresso e il file originale (ad esempio, più i file sono incomprimibili, più alto è il punteggio). Un file perfettamente incomprimibile che cresce leggermente o per niente conterrà 0.

2
La risposta alla pesca a traina: Passaggio 1: calcolare la quantità di spazio libero su disco disponibile, quindi dividerlo per la dimensione del file per ottenere N. Passaggio 2: aggiungere il file a se stesso N volte e aggiungere il numero N. Passaggio 3: capire che c'è non c'è più spazio per comprimere il file, ma finisce con una differenza assoluta nelle dimensioni dei file di diversi terrabyte (o più) .... [Per invertire, leggi N dalla fine del file e riduci il file a 1/3 della dimensione. ]
MT0,

@ MT0: Ah, penso che la soluzione sia che le differenze non dovrebbero essere assolute. Se il file modificato è più grande, è necessario sottrarre punti.
Claudiu,

@ MT0 se modifichi il file per renderlo di un terabyte grande, il tuo punteggio sarà di 1 terabyte ... piuttosto male quando stai cercando di giocare a golf.
Nick T,

@ MT0 Ho aggiunto un chiarimento al post, aiuta?
Nick T,

2
Un cavillo. Il compressore potrebbe creare un file più grande se t è particolarmente incomprimibile. In questo caso dovresti essere ricompensato, non punito, no?
Claudiu,

Risposte:


8

Python, punteggio = 120

import sys,hashlib
i=0
for c in sys.stdin.read():sys.stdout.write(chr(ord(c)^ord(hashlib.md5(str(i)).digest()[0])));i+=1

Crea un pad una tantum usando md5 in modalità contatore . xor il file con esso. Ciò ha il vantaggio che i file originali e danneggiati hanno le stesse dimensioni e che il disgregatore e il riparatore sono lo stesso programma.

I file compressi interrotti sono più grandi degli originali.


Ho modificato il punteggio in modo che se i file zippati sono più grandi delle loro controparti originali non sei penalizzato e ottengono solo un punteggio 0. Non sei sicuro di quale sia stata la differenza per i tuoi file, ma potresti voler aggiornare il punteggio
Nick T

@NickT: aggiornato.
Keith Randall,

8

C, 51 = 51 + 0 + 0 + 0 + 0

main(c){for(;c=~getchar();putchar(~c^rand()>>23));}

Sotto i trucchi del golf , questo programma esegue il loop per ogni byte nell'input standard e esegue in esclusiva o con un pad infinito da rand (). Ho provato questo con rand () in libc di OpenBSD 5.5.

Uso:

./scramble <orig >scrambled
./scramble <scrambled >orig.copy

Per testare il mio programma, ho scritto uno script di shell test.sh (57 righe) per compilare il mio programma e calcolare il mio punteggio.

$ sh test.sh
[1/4] Compiling scramble...
/tmp//ccbcB43x.o(.text+0x6): In function `main':
: warning: rand() isn't random; consider using arc4random()
[2/4] Scrambling files...
[3/4] Compressing scrambled files...
[4/4] Checking descrambler...
SCORE: 51=51+0+0+0+0
You may wish to rm -rf tmp.9Tjw89dgCs
$ ls -l tmp.9Tjw89dgCs/
total 43032
-rw-r--r--  1 kernigh  kernigh  1659874 May 28 17:23 filament.jpg.cp
-rw-r--r--  1 kernigh  kernigh  1659874 May 28 17:23 filament.jpg.sc
-rw-r--r--  1 kernigh  kernigh  1660016 May 28 17:23 filament.jpg.sc.xz
-rw-r--r--  1 kernigh  kernigh  5589891 May 28 17:23 pg100.txt.cp
-rw-r--r--  1 kernigh  kernigh  5589891 May 28 17:23 pg100.txt.sc
-rw-r--r--  1 kernigh  kernigh  5590232 May 28 17:23 pg100.txt.sc.xz
-rwxr-xr-x  1 kernigh  kernigh     8564 May 28 17:23 scramble

Note su rand () e il turno giusto

Qualsiasi algoritmo di compressione non può comprimere dati casuali. Posso nascondere pg100.txt e filament.jpg come dati casuali se li mescolo con un codice di flusso .

La mia prima idea era di esclusivo o di testo in chiaro con pad per make testo cifrato , quindi memorizzare sia testo cifrato e pad nel file criptato. Ciò aumenterebbe la dimensione del file e aumenterebbe il mio punteggio. La scelta ovvia è usare lo stesso pad per ogni file e archiviare solo il testo cifrato nel file criptato. Se chiamo semplicemente rand (), usa un seed predefinito di 1 e crea sempre lo stesso pad .

OpenBSD 5.5 definisce rand () in stdlib.h e rand.c :

/* from stdlib.h */
#define RAND_MAX    0x7fffffff

/* from rand.c */
static u_int next = 1;

int
rand_r(u_int *seed)
{
    *seed = *seed * 1103515245 + 12345;
    return (*seed % ((u_int)RAND_MAX + 1));
}

int
rand(void)
{
    return (rand_r(&next));
}

Questo è un generatore congruenziale lineare . Il grande difetto è che i bit bassi hanno brevi periodi. Il 1 ° bit ha un periodo di 2: se lanci una moneta rand()&1, andrebbe testa, croce, testa, croce e così via. L'ennesimo bit ha un periodo di 2 n . Ci sono 31 bit, quindi l'intera sequenza ha un periodo di 2 31 .

LZMA2 può trovare modelli in brevi periodi e comprimerli. Il codice più breve ~c^rand()accetta gli 8 bit bassi e non impedisce la compressione. Il giusto spostamento in ~c^rand()>>9aiuta, ma non abbastanza. Io uso ~c^rand()>>23.

  • ~c PUNTEGGIO: 4227957 = 40 + 0 + 0 + 4019391 + 208526
  • ~c^rand() PUNTEGGIO: 2474616 = 47 + 0 + 0 + 2463735 + 10834
  • ~c^rand()>>9 PUNTEGGIO: 350717 = 50 + 0 + 0 + 350667 + 0
  • ~c^rand()>>23 PUNTEGGIO: 51 = 51 + 0 + 0 + 0 + 0

5

BrainFuck : 129 (129 + 0 + 0 + 0 + 0) *

random.bf (feed di riga aggiunti per leggibilità)

,+[->>>>++[<++++++++[<[<++>-]>>[>>]+>>+[-[->>+<<<[<[<<]<
+>]>[>[>>]]]<[>>[-]]>[>[-<<]>[<+<]]+<<]<[>+<-]>>-]<[-<<+
>>]<<.,+[->]>>>]]

Per creare unrandom.bfè necessario modificare l'ultimo + nella seconda riga.

La maggior parte del codice si basa sul generatore di numeri casuali basato su Rule30 di Daniel B Cristofani, adattato per aggiungere il numero a ciascun ingresso e per terminare quando non vi sono più input.

* Ho testato i byte che ha elaborato finora 212992 (elaborati dopo 12 ore) ed entrambi i file si trasformano in un file compresso 213064. Immagino che potrebbe essere fatto entro la fine della settimana per certo, ma non voglio aspettare con la pubblicazione. Aggiornerò il punteggio se è sbagliato, ma manterrò la soluzione poiché la regola 30 oscilla!

Curiosità: la Regola 30 è stata scoperta da Stephen Wolfram nel 1983 e secondo Wikipedia è usata per produrre numeri casuali in Mathematica.

Compilazione e funzionamento:

Utilizza tempo e spazio esponenziali (scorre oltre 32 celle in più per carattere elaborate), quindi richiede un runtime BrainFuck che abbia almeno 178.876.517 celle per codificare il file Shakespear, non tratti non ascii come unicode, abbia celle più larghe di 8 bit e usi -1 come eof (per differire tra 255 e -1). Di solito uso interpreti di altre persone, ma questa volta ho bisogno di essere una spina e promuovere il mio:

jitbf --eof -1 -b 16 -c 200000000 random.bf < pg100.txt > pg100.txt.ran
jitbf --eof -1 -b 16 -c 200000000 random.bf < Glühwendel_brennt_durch.jpg > Glühwendel_brennt_durch.jpg.ran

jitfb compila BrainFuck in C ottimizzato e abusa perl Inline :: C per eseguirlo. È in bundle con il mio compilatore Extended BrainFuck . Con la dimensione e la larghezza della cella nell'argomento assegnerà circa 400 MB.


3

CJam, 22 byte

G,~q{5$H$+255%_@^o}/];

Questo utilizza un generatore di Fibonacci ritardato con relazione di ricorrenza s n = (s n-5 + s n-16 )% 255 (che ho selezionato per errore, ma funziona comunque) e un seme banale per generare un flusso pseudo-casuale di byte , che quindi XORs con l'input.

Ho testato il mio codice con CJam 0.6 , che è stato pubblicato il 1 maggio 2014.

Come funziona

G,~                    e# Dump 0, 1, ... and 15 on the stack.
   q                   e# Read from STDIN.
    {             }/   e# For each character in the input.
     5$H$              e# Copy the sixth and 19th element from the stack.
         +255%         e# Push their sum modulo 255.
              _@       e# Duplicate and rotate the character on top.
                ^o     e# XOR and print.
                    ]; e# Clear the stack.

Punto

$ LANG=en_US
$ alias cjam='java -jar /usr/local/share/cjam/cjam-0.6.jar'
$ cjam thwart.cjam < pg100.txt > pg100.txt~
$ cjam thwart.cjam < pg100.txt~ > pg100.txt~~
$ diff -s pg100.txt pg100.txt~~
Files pg100.txt and pg100.txt~~ are identical
$ cjam thwart.cjam < Gluehwendel_brennt_durch.jpg > Gluehwendel_brennt_durch.jpg~
$ cjam thwart.cjam < Gluehwendel_brennt_durch.jpg~ > Gluehwendel_brennt_durch.jpg~~
$ diff -s Gluehwendel_brennt_durch.jpg Gluehwendel_brennt_durch.jpg~~
Files Gluehwendel_brennt_durch.jpg and Gluehwendel_brennt_durch.jpg~~ are identical
$ xz -kz5 pg100.txt~ Gluehwendel_brennt_durch.jpg~
$ wc -c thwart.cjam pg100.txt* Gluehwendel_brennt_durch.jpg*
      22 thwart.cjam
 5589889 pg100.txt
 5589889 pg100.txt~
 5589889 pg100.txt~~
 5590232 pg100.txt~.xz
 1659874 Gluehwendel_brennt_durch.jpg
 1659874 Gluehwendel_brennt_durch.jpg~
 1659874 Gluehwendel_brennt_durch.jpg~~
 1660016 Gluehwendel_brennt_durch.jpg~.xz
28999559 total

3

PHP, 117 + 0 + 0 + 0 + 0 = 117

Perché affideresti davvero il compito di manipolare i tuoi dati oltre il riconoscimento in qualsiasi altra lingua?

<?=substr(gmp_export(gmp_invert(2*gmp_import($s=stream_get_contents(STDIN))+1,$m=2*gmp_pow(256,strlen($s)))/2+$m),1);

Mentre tutte le altre soluzioni si basano su costruzioni "sicure" come "generatori di numeri casuali" o "crittografia di livello militare", questa semplicemente interpreta le stringhe come numeri dispari modulo 2⋅256 ^ lunghezza e calcola il loro inverso modulare .

demo:

$ php thwart.php < 100.txt.utf-8 > 100.txt.utf-8~
$ php thwart.php < 100.txt.utf-8~ > 100.txt.utf-8~~
$ diff -s 100.txt.utf-8 100.txt.utf-8~~
Files 100.txt.utf-8 and 100.txt.utf-8~~ are identical
$ php thwart.php < Glühwendel_brennt_durch.jpg > Glühwendel_brennt_durch.jpg~
$ php thwart.php < Glühwendel_brennt_durch.jpg~ > Glühwendel_brennt_durch.jpg~~
$ diff -s Glühwendel_brennt_durch.jpg Glühwendel_brennt_durch.jpg~~
Files Glühwendel_brennt_durch.jpg and Glühwendel_brennt_durch.jpg~~ are identical
$ xz -kz5 100.txt.utf-8~ Glühwendel_brennt_durch.jpg~
$ wc -c *
 5589889 100.txt.utf-8
 5589889 100.txt.utf-8~
 5590232 100.txt.utf-8~.xz
 5589889 100.txt.utf-8~~
 1659874 Glühwendel_brennt_durch.jpg
 1659874 Glühwendel_brennt_durch.jpg~
 1660016 Glühwendel_brennt_durch.jpg~.xz
 1659874 Glühwendel_brennt_durch.jpg~~
     117 thwart.php
28999654 total

2

shell script, 203

id|gpg --batch --passphrase-fd 0 --personal-compress-preferences Uncompressed $1 $2

Eseguendolo:

% sh break.sh -c pg100.txt                       
% sh break.sh -d pg100.txt.gpg > pg100.txt-original
gpg: CAST5 encrypted data
gpg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected
% diff -s pg100.txt pg100.txt-original
Files pg100.txt and pg100.txt-original are identical
% sh break.sh -c Glühwendel_brennt_durch.jpg
% sh break.sh -d Glühwendel_brennt_durch.jpg.gpg > Glühwendel_brennt_durch.jpg-original
gpg: CAST5 encrypted data
gpg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected
% diff -s Glühwendel_brennt_durch.jpg Glühwendel_brennt_durch.jpg-original
Files Glühwendel_brennt_durch.jpg and Glühwendel_brennt_durch.jpg-original are identical
% xz -kz5 Glühwendel_brennt_durch.jpg.gpg 
% xz -kz5 pg100.txt.gpg 
% ls -ln
total 28340
-rw-r--r-- 1 1000 1000      84 May 24 04:33 break.sh
-rw-r--r-- 1 1000 1000 1659874 Jan 19 17:22 Glühwendel_brennt_durch.jpg
-rw-r--r-- 1 1000 1000 1659943 May 24 04:46 Glühwendel_brennt_durch.jpg.gpg
-rw-r--r-- 1 1000 1000 1660084 May 24 04:46 Glühwendel_brennt_durch.jpg.gpg.xz
-rw-r--r-- 1 1000 1000 1659874 May 24 04:46 Glühwendel_brennt_durch.jpg-original
-rw-r--r-- 1 1000 1000 5589891 May 24 03:55 pg100.txt
-rw-r--r-- 1 1000 1000 5589941 May 24 04:43 pg100.txt.gpg
-rw-r--r-- 1 1000 1000 5590284 May 24 04:43 pg100.txt.gpg.xz
-rw-r--r-- 1 1000 1000 5589891 May 24 04:43 pg100.txt-original

Non molto portatile, ma potrebbe essere realizzato al costo di un paio di byte. Richiede PGP (sarebbe anche possibile un'implementazione con OpenSSL). Probabilmente è possibile salvare la differenza di ~ 50 byte tra il file codificato e l'originale.

punteggio:

84 + abs (1659874 - 1659943) + max (1659874 - 1660084, 0) + abs (5589891 - 5589941) + max (5589891 - 5590284, 0) = 203


1

Python, punteggio = 183 + 7 + 6 + 0 + 0 = 196

Il punteggio ti penalizza per aver reso il file completamente non comprimibile, da allora il file compresso è più grande del sovraccarico di compressione. Quindi il mio programma li rende leggermente meno del tutto non comprimibile:

import sys
from random import randint as J,seed
x=sys.stdin.read()
seed(ord(x[1]))
n=int(2362*J(1,2)**2.359)
sys.stdout.write(x[:n]+''.join(chr(ord(c)^J(0,255))for c in x[n:]))

Risultato:

Laxori@Laxori-PC /cygdrive/f/Programming/lzkill
$ cat photo.jpg | python break.py > photo.jpg~; cat photo.jpg~ | python break.py > photo.jpg~~; diff photo.jpg photo.jpg~~; xz -kz5 photo.jpg~

Laxori@Laxori-PC /cygdrive/f/Programming/lzkill
$ cat pg100.txt | python break.py > pg100.txt~; cat pg100.txt~ | python break.py > pg100.txt~~; diff pg100.txt pg100.txt~~; xz -kz5 pg100.txt~

Laxori@Laxori-PC /cygdrive/f/Programming/lzkill
$ ls -l
total 28337
----------+ 1 Laxori mkpasswd     183 2014-05-24 13:43 break.py
----------+ 1 Laxori mkpasswd 5589891 2014-05-23 19:19 pg100.txt
-rw-r--r--+ 1 Laxori mkpasswd 5589891 2014-05-24 13:45 pg100.txt~
-rw-r--r--+ 1 Laxori mkpasswd 5589884 2014-05-24 13:45 pg100.txt~.xz
-rw-r--r--+ 1 Laxori mkpasswd 5589891 2014-05-24 13:45 pg100.txt~~
----------+ 1 Laxori mkpasswd 1659874 2014-05-23 19:19 photo.jpg
-rw-r--r--+ 1 Laxori mkpasswd 1659874 2014-05-24 13:44 photo.jpg~
-rw-r--r--+ 1 Laxori mkpasswd 1659880 2014-05-24 13:44 photo.jpg~.xz
-rw-r--r--+ 1 Laxori mkpasswd 1659874 2014-05-24 13:44 photo.jpg~~

Laxori@Laxori-PC /cygdrive/f/Programming/lzkill
$ python
Python 2.5.2 (r252:60911, Dec  2 2008, 09:26:14)
[GCC 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 183 + abs(5589891-5589884) + abs(1659874-1659880)
196

Con le regole attuali, non vi è alcuna penalità per un file compresso di dimensioni maggiori. Le regole sono cambiate? In tal caso, tale cambiamento non è stato giusto per questo programma.
kernigh,

@kernigh: Sì, sono cambiati dopo che l'ho pubblicato. Davvero avrebbero dovuto essere come sono ora dall'inizio.
Claudiu,
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.