Entropy and Prefix-Free Coding
A lossless compressor exploits a probability model rather than making symbols intrinsically smaller. An event of probability $p$ carries self-information $I(p)=-\log_2p$ bits. For probabilities $p_1,\dots,p_\sigma$, the entropy $H(X)=-\sum_i p_i\log_2p_i$ is the ideal expected information per symbol under the model. Real formats also pay for model descriptions, block metadata, checksums, and finite representations.
A binary prefix-free code assigns no symbol a codeword that prefixes another. If lengths are $\ell_1,\dots,\ell_\sigma$, Kraft–McMillan requires $\sum_i2^{-\ell_i}\le 1$: every length-$\ell$ word occupies a fraction $2^{-\ell}$ of a binary tree's leaves, and codeword subtrees must be disjoint.
Huffman repeatedly combines the two least-frequent nodes and minimizes expected length among binary prefix codes for the supplied frequencies. Ties can yield different trees with equal cost. A canonical code transmits only lengths: sort by (length, symbol), assign consecutive integers within a length, and left-shift when length increases. This produces deterministic compact tables.
Arithmetic, Range, and Context Coding
Arithmetic coding represents a complete message as a subinterval of $[0,1)$. A model partitions the current interval by cumulative probability; encoding selects the next symbol's region repeatedly and finally emits a value inside the end-symbol interval. Unlike a prefix code, this amortizes fractional information across symbols.
Range coders implement the same idea with integer low and high bounds. They emit shared leading bits or bytes, renormalize before precision is exhausted, and specify carry and underflow rules exactly. A static model transmits fixed frequencies; an adaptive model updates agreed initial counts after each symbol. Context models condition probabilities on preceding symbols and may back off to shorter contexts when evidence is sparse.
Decoding is symmetric: map the coded value to a cumulative-frequency position, select the symbol, narrow the interval exactly as the encoder did, update the model, and renormalize. One disagreement in frequencies or update order corrupts every subsequent decision.
Range-Coding Trace
Encode AB followed by END using $P(A)=0.5$, $P(B)=0.3$, and $P(END)=0.2$. The cumulative ranges are $A:[0,0.5)$, $B:[0.5,0.8)$, and $END:[0.8,1)$.
- Begin at $[0,1)$; selecting
Ayields $[0,0.5)$. - Its width is $0.5$; selecting
Byields $[0+0.5(0.5),0+0.5(0.8))=[0.25,0.40)$. - Its width is $0.15$; selecting
ENDyields $[0.25+0.15(0.8),0.25+0.15)=[0.37,0.40)$. - The value $0.375$ lies inside the final interval and identifies the message.
- The decoder locates that value successively within the ranges for
A,B, andEND, reproducing the same updates before stopping.
Run-Length and Dictionary Compression
RLE replaces a maximal run with a symbol-count pair. It excels on masks, repeated pixels, sparse bitmaps, and transformed data, but can expand alternating input; practical formats mix literal blocks and run blocks.
LZ77 uses already decoded text as a sliding dictionary and emits literals or (distance, length) references. Overlapping references can generate long repetitions from short seeds. Greedy LZ factorization selects the longest previous factor at each position, while practical encoders limit match search using hash chains or suffix-based indexes.
LZ78 builds explicit phrases and emits (prior phrase, next symbol). LZW makes the initial single-symbol dictionary implicit and emits phrase codes while encoder and decoder add entries synchronously. Width growth, reset rules, and the next-not-yet-created-code case belong to the format contract.
| Method | Dictionary source | Emitted representation | Streaming behavior | Decoder requirements | Best-case structure |
|---|---|---|---|---|---|
| RLE | Current run only | Symbol-count or literal/run blocks | One pass with constant state | Run syntax and count limits | Long equal-symbol runs |
| LZ77 | Sliding window of decoded bytes | Literals and (distance, length) | Streaming with bounded history | Window size and overlap-copy rules | Nearby repeated substrings |
| LZ78 | Explicit prior phrases | (phrase index, next symbol) | One pass; dictionary grows or resets | Initial entry and insertion order | Hierarchically recurring phrases |
| LZW | Agreed alphabet followed by synchronized phrases | Integer phrase codes | Streaming with growing code width | Alphabet, width changes, resets, special missing-code case | Repeated phrase sequences |
| Greedy LZ factorization | Conceptually the complete previous prefix | Longest previous factors and literals | Online with an index over prior text | Factor sources, lengths, and literal convention | Long previous factors |
The Burrows-Wheeler Compression Pipeline
With a unique sentinel smaller than every alphabet symbol, the BWT sorts cyclic rotations and emits their final column plus a primary-row convention. It does not compress by itself; it clusters symbols preceding similar contexts, creating local runs.
For inversion, stable sorting the last column $L$ produces the first column $F$. The $k$-th occurrence of a symbol in $L$ corresponds to its $k$-th occurrence in $F$. Repeated LF steps reconstruct the text. Cumulative counts and occurrence ranks implement the mapping without storing rotations.
Move-to-front replaces each BWT symbol by its current position in an alphabet list and moves it to the front, turning clusters into many small ranks and zeros. RLE compresses zero runs, then Huffman, arithmetic, or range coding encodes the resulting distribution. Decompression reverses the stages exactly.
flowchart LR
TEXT["Text plus sentinel"]
BWT["Burrows-Wheeler transform"]
MTF["Move-to-front ranks"]
RLE["Run-length encoding"]
ENTROPY["Entropy coding"]
STREAM["Compressed bitstream"]
TEXT -->|"reversible permutation"| BWT
BWT -->|"clusters become small ranks"| MTF
MTF -->|"collapse zero runs"| RLE
RLE -->|"encode symbol probabilities"| ENTROPY
ENTROPY --> STREAM
STREAM -. decode .-> ENTROPY
ENTROPY -. expand .-> RLE
RLE -. inverse MTF .-> MTF
MTF -. LF mapping .-> BWT
BWT -. restore .-> TEXT
Compression Engineering and Search
Compression ratio must be balanced against encode time, decode throughput, memory, latency, and parallelism. Larger blocks expose longer repetition; smaller independent blocks improve random access, bounded memory, parallel decoding, and corruption containment.
DEFLATE combines bounded-window LZ77 with fixed or transmitted Huffman tables. A damaged length, distance, or entropy symbol can desynchronize a block, so containers add checksums, frames, restart points, and lengths. Grammar compression represents repeated substrings as nonterminals in a straight-line grammar; it can be excellent for repetitive collections, though construction and indexed navigation are more complex.
Ordinary streams are not inherently seekable because late output may depend on earlier dictionary and model state. Seekable formats add independently decodable frames and offset indexes. Compressed indexes retain rank, select, sampling, or grammar-navigation data so search and extraction can run without reconstructing the whole text.
The BWT also powers the FM-index: cumulative counts and rank queries extend a suffix-array interval for pattern suffix $P$ backward to the interval for $cP$. Run-length or wavelet-based BWT storage therefore supports both compression and search through the same LF-mapping structure.
Next in the Series
In Algebraic, Randomized, Dynamic & Multidimensional Methods, we'll explore FFT matching, fingerprints, editable indexes, streaming strings, grids, trees, and specialized alphabets.