Livro de SW: sec.5.5, p.810-852. Website do livro: resumo sec.5.5, slides. Veja também a página http://algs4.cs.princeton.edu/code/, que tem o código fonte, a API, e dados de teste de todos os programas do livro.
Esta página trata de conceitos básicos de compressão e do algoritmo de comprimento-de-carreira (run-length encoding). O algoritmo de Huffman é discutida na página seguinte.
Resumo:
Pré-requisitos:
fluxo de bits, sem dar atenção ao significado que os blocos de 8 bits (ou 16 bits, ou 32 bits) possam ter para o usuário.
… mas foi produzido pelo programa a seguir. Portanto, o código do programa é uma versão comprimida do fluxo de bits da figura (com taxa de compressão inferior a 2%)!
public class RandomBits {public static void main(String[] args)
{int x=11111; for(int i=0;i<1000000;i++){x=x*314159+218281;
BinaryStdOut.write(x>0);} BinaryStdOut.close();}}
public static void compress() {
   Alphabet DNA = new Alphabet("ACTG");
   int w = DNA.lgR();                  // w == 2
   String s = BinaryStdIn.readString(); 
   int N = s.length();
   BinaryStdOut.write(N);
   for (int i = 0; i < N; i++) {
      int d = DNA.toIndex(s.charAt(i));
      BinaryStdOut.write(d, w);        // escreve 2 bits
   }
   BinaryStdOut.close();
}
public static void expand() {
   Alphabet DNA = new Alphabet("ACTG");
   int w = DNA.lgR();                    // w == 2
   int N = BinaryStdIn.readInt();
   for (int i = 0; i < N; i++) {   
      char c = BinaryStdIn.readChar(w);  // lê 2 bits e
      BinaryStdOut.write(DNA.toChar(c)); // escreve um char
   }
   BinaryStdOut.close();
}
public class Genome { public static void compress() { /* veja acima */ } public static void expand() { /* veja acima */ } public static void main(String[] args) { if (args[0].equals("-")) compress(); if (args[0].equals("+")) expand(); } }
% more genomeTiny.txt
ATAGATGCATAGCGCATAGCTAGATGTGCTAGC
% java BinaryDump 64 < genomeTiny.txt
0100000101010100010000010100011101000001010101000100011101000011
0100000101010100010000010100011101000011010001110100001101000001
0101010001000001010001110100001101010100010000010100011101000001
0101010001000111010101000100011101000011010101000100000101000111
01000011
264 bits
% java Genome - < genomeTiny.txt
??
Não dá pra ver um fluxo de bits diretamente na saída padrão… É precisa passar o fluxo por BinaryDump ou HexDump:
% java Genome - < genomeTiny.txt | java BinaryDump 64 0000000000000000000000000010000100100011001011010010001101110100 1000110110001100101110110110001101000000 104 bits % java Genome - < genomeTiny.txt | java HexDump 8 00 00 00 21 23 2d 23 74 8d 8c bb 63 40 104 bits
Compressão seguida de expansão reproduz o fluxo original:
% java Genome - < genomeTiny.txt > genomeTiny.2bit % java Genome + < genomeTiny.2bit ATAGATGCATAGCGCATAGCTAGATGTGCTAGC % java Genome - < genomeTiny.txt | java Genome + ATAGATGCATAGCGCATAGCTAGATGTGCTAGC
% java PictureDump 512 100 < genomeVirus.txt 50000 bits% java Genome - < genomeVirus.txt | java PictureDump 512 25
12536 bits
    0000000000000001111111000000011111111111
Assim, a cadeia pode ser representada pela sequência de números 15 7 7 11. Se usarmos 8 bits para representar cada um desses números em binário, teremos uma cadeia de apenas 32 bits (ignore os espaços):
    00001111 00000111 00000111 00001011
% java BinaryDump 0 < abra.txt 96 bits % java RunLength - < abra.txt | java HexDump 24 01 01 05 01 01 01 04 01 02 01 01 01 02 01 02 01 05 01 01 01 04 02 01 01 05 01 01 01 03 01 03 01 05 01 01 01 04 01 02 01 01 01 02 01 02 01 05 01 02 01 04 01 416 bits
qem uma tela de 32 por 48 pixels:
% java PictureDump 32 48 < q32x48.bin 1536 bits![]()
[Alguns dos números do lado direito da figura estão errados na linha 32 e seguintes.] Para produzir a cadeia de bits original, emende cada linha do binary dump com a seguinte. Os comprimentos de carreira são, portanto, 79 7 22 15 15 4 4 9 13 4 9 ... 19 14 65 .
% java RunLength - < q32x48.bin > q32x48.bin.rle % java HexDump 16 < q32x48.bin.rle 4f 07 16 0f 0f 04 04 09 0d 04 09 06 0c 03 0c 05 0b 04 0c 05 0a 04 0d 05 09 04 0e 05 09 04 0e 05 08 04 0f 05 08 04 0f 05 07 05 0f 05 07 05 0f 05 07 05 0f 05 07 05 0f 05 07 05 0f 05 07 05 0f 05 07 05 0f 05 07 05 0f 05 07 06 0e 05 07 06 0e 05 08 06 0d 05 08 06 0d 05 09 06 0c 05 09 07 0b 05 0a 07 0a 05 0b 08 07 06 0c 14 0e 0b 02 05 11 05 05 05 1b 05 1b 05 1b 05 1b 05 1b 05 1b 05 1b 05 1b 05 1b 05 1b 05 1b 05 1a 07 16 0c 13 0e 41 1144 bits
public static void compress() {
   char cnt = 0;
   boolean b, old = false;
   while (!BinaryStdIn.isEmpty()) {
      b = BinaryStdIn.readBoolean();
      if (b != old) {
         BinaryStdOut.write(cnt);
         cnt = 0;
         old = !old;
      }
      else {
         if (cnt == 255) {
            BinaryStdOut.write(cnt);
            cnt = 0;
            BinaryStdOut.write(cnt); // indica carreira de 0 bits
         }
      }
      cnt++;
   }
   BinaryStdOut.write(cnt);
   BinaryStdOut.close();
}
public static void expand() {
   boolean b = false;
   while (!BinaryStdIn.isEmpty()) {
      char cnt = BinaryStdIn.readChar(); // comprimento de uma carreira
      for (int i = 0; i < cnt; i++)
         BinaryStdOut.write(b);          // sai um único bit
      b = !b;
   }
   BinaryStdOut.close();
}
A taxa de compressão diminui linearmente com o aumento da resolução!
% java PictureDump 32 48 < q32x48.bin 1536 bits% java RunLength - < q32x48.bin | java BinaryDump 0 1144 bits % java PictureDump 64 96 < q64x96.bin 6144 bits
% java RunLength - < q64x96.bin | java BinaryDump 0 2296 bits