The floors

The bits a coder emits

A stream of 16,384 symbols with a zeroth-order entropy of 3.891 bits per symbol was coded by a Huffman coder into 3.937 and by an arithmetic coder into 3.898, and neither went under 3.891 because neither can. That floor is a third kind of limit, the first that is a property of a model rather than of a question, and the same stream has a different one under every model of it.

This site has two floors already. The floor under every comparison sort says no comparison sort averages fewer than log2(n!)\log_2(n!) comparisons. A floor on the bits says no structure answering approximate membership at a rate ε\varepsilon uses fewer than nlog2(1/ε)n\log_2(1/\varepsilon) bits of space.

Both are properties of the question. Sorting has n!n! possible answers and that number does not care what anybody assumes; the membership bound counts the sets that must be distinguishable and neither does that.

The third floor is not like that, and the difference is the whole subject of this field.

Bits per symbol on words from a fixed vocabulary, 16,384 symbolsThe dashed line is the zeroth-order entropy of this stream, 3.892 bits per symbol: the floor for any coder that gives each symbol a code and looks at nothing else. Huffman and arithmetic coding are both such coders and neither is under it. The dictionary coders are not, and LZSS (window 4096) and LZ78 finish below it — which is not a violation of anything, because the line is the floor for a different model than the one they use.bits per symbolhuffman3.9371.01× the floorarithmetic (adaptive)3.8981.00× the floorLZSS (window 4096)2.1130.54× the floorLZ783.1470.81× the floorH₀ = 3.89216,384 symbols · alphabet 21 · floor shown is H0 = 3.892 bits/symbolmodel: order 0 · Words from a fixed vocabularybest here: 2.113 bits/symbol
Fig. 1 Four coders on the same 16,384 symbols. The dashed line is the stream’s zeroth-order entropy, 3.891 bits per symbol. Huffman and arithmetic coding are above it, because a coder that assigns each symbol a code and looks at nothing else cannot go under it. Two of the four finish well below — which is not a violation of anything, because the line is not their floor.

What a coder is being measured on

The seventh counter on this site counts bits emitted: a coder is written against a BitSink and cannot produce output without the output being measured.

That sounds like a technicality and it is the difference between this field and a recitation. Nothing here computes log2p-\sum \log_2 p and calls it a compression result. Every number on this page came out of a coder that wrote bits one at a time, and every coder here has a decoder that reads them back and is required to reproduce the input exactly. A compression ratio quoted from a coder that was never decoded is a claim about a program nobody has shown to work, and the failure mode is not a crash — it is a bit count that is too small, which reads as a better result.

The site has counted bits twice before and neither time was this. Coins counts bits consumed, a resource an algorithm spends, in the same way it spends comparisons. The Bloom filter bound is about bits stored. These are bits emitted: the coder’s output, the thing it exists to minimise.

The floor

Shannon’s source coding theorem, in the form this page needs: a stream whose symbols are drawn independently with probabilities pip_i cannot be coded, on average, in fewer than

H=ipilog2piH = -\sum_i p_i \log_2 p_i

bits per symbol. The quantity log2p-\log_2 p is what a symbol of probability pp is worth: a symbol that happens half the time is worth one bit, one that happens a thousandth of the time is worth 9.97, and one that always happens is worth nothing.

For the hero figure’s stream — text built from a fixed vocabulary, 21 distinct characters — H0H_0 is 3.891 bits per symbol, or 63,749 bits for the whole stream, or 7,969 bytes for a stream of 16,384 characters.

The measured results:

  • Arithmetic coding, adaptive: 3.898 bits per symbol. Seven thousandths of a bit above the floor.
  • Huffman: 3.937. Forty-six thousandths above.
  • LZSS: 2.113. Well below.
  • LZ78: 3.147. Also below.

The first two are the ones the floor applies to and neither is under it. The last two are below a floor and nothing is wrong; the rest of this essay and the two after it are about why.

Why the symbol coders cannot get under it

Huffman and arithmetic coding are both symbol-wise: each looks at one symbol, consults a probability for it, and emits accordingly. The floor for such a coder is the entropy of the distribution it consults, and it is a floor for an unimprovable reason — if a code assigned some symbols fewer bits than log2p-\log_2 p without assigning others more, the total number of distinct codewords available would exceed the number of distinct messages, and two messages would share a code.

That constraint has a name and an exact form. Kraft’s inequality: for any prefix code with lengths i\ell_i,

i2i1\sum_i 2^{-\ell_i} \le 1

with equality when no bit pattern is wasted. Every Huffman code built here is checked against it, and the check is the most load-bearing one in the file: a sum under 1 means the code is spending bits it did not have to, and every gap-to-the-floor figure would be measuring the waste rather than the algorithm; a sum over 1 means the code is not decodable at all, and the decoder would discover it on an unlucky stream rather than on the test.

A Huffman code for 8 symbolsEvery leaf is a symbol and its depth is its code length in bits, so the picture and the code are the same object. The mean length is 2.508 bits against an entropy of 2.458 — a gap of 0.050 of a bit, which is what rounding every symbol to a whole number of levels costs. Kraft's sum over the lengths is exactly 1, which is what makes it a complete prefix code: no bit pattern is wasted and none is ambiguous.depth = code length in bits · left edge 0, right edge 1010101010101010123456a212 bitst302 bitso153 bitsi94 bitsh16 bitss36 bitsn55 bitse422 bitsmean 2.508 bits/symbol · entropy 2.458 · the rarest is worth 6.98 bits and is charged 64 symbols are charged more than they are worth and 4 lessmodel: order 0, from the stated weightsKraft sum = 1.000
Fig. 2 A Huffman code drawn as what it is. Every leaf is a symbol, its depth is its code length, and the whole tree is the code. The mean length is 2.508 bits against an entropy of 2.458 — a gap of exactly 0.050 of a bit, which is what rounding every symbol to a whole number of levels costs on this distribution. Kraft’s sum over the eight lengths is exactly 1.

The rounding, and one symbol that gets a bargain

The tree above is worth reading symbol by symbol, because it shows something the summary hides.

symbol probability worth, in bits charged
e 0.3333 1.585 2
t 0.2381 2.070 2
a 0.1667 2.585 2
o 0.1190 3.070 3
i 0.0714 3.807 4
n 0.0397 4.655 5
s 0.0238 5.392 6
h 0.0079 6.977 6

Most symbols are charged more than they are worth, which is the rounding, and it is what the 0.050-bit gap is made of. a is charged 2 for something worth 2.585 — a bargain, and h is charged 6 for something worth 6.977, a larger one.

That is the property that makes Huffman’s construction non-obvious. It is not rounding each symbol up; some symbols come out ahead and others behind, and the algorithm is optimal because the weighted total is minimal, not because any individual assignment is. A greedy rule that rounded each log2p-\log_2 p to the nearest integer would produce a code with a Kraft sum away from 1 and would be worse.

Huffman’s guarantee is that the mean sits in [H,H+1)[H, H+1): never under the entropy, never a whole bit above it. Both ends are checked in the gate, on the stream where the gap is widest, because a violation of the first would mean the entropy calculation is wrong and a violation of the second would mean the code is.

How the tree gets built, and why the greedy rule is right

The construction is three lines and its correctness is the surprising part.

Put every symbol in a bag with its frequency as a weight. Repeatedly take the two lightest items, join them under a new node whose weight is their sum, and put that node back. When one item remains, it is the root, and each symbol’s depth is its code length.

The reason it is optimal is an exchange argument. In any optimal code the two least frequent symbols must be siblings at the deepest level — if they were not, swapping one of them with whatever is down there would not increase the total, since the deeper symbol is at least as frequent. So joining them first cannot be wrong, and the remaining problem is the same problem with one fewer symbol.

That is the whole proof, and it is worth having on the page because it is a proof about a greedy algorithm being globally optimal, which is rare enough that the ones that hold are worth collecting. This site has one other: building a heap from the bottom, where taking the cheap local step in the right order gives a linear total rather than a linearithmic one.

There is a caveat the implementation had to handle. A Huffman code is not unique. Ties in the weights can be broken either way and produce codes with the same mean length and different individual assignments. The construction here breaks ties by symbol order and by insertion index rather than by whatever the sort happens to do, so the code is the same on every machine and the figures above reproduce byte for byte — which is not an aesthetic preference on a site whose figures carry numbers in their captions.

The check that the bits are real bits

The gate runs every coder in this file and requires each one to decode.

assertEveryCoderRoundTrips codes 2,048 symbols with the Huffman coder, the adaptive arithmetic coder, LZSS and the Burrows–Wheeler transform, then reads each back and compares against the input character by character. It is the least interesting check in the file to read and the one most likely to catch a real defect, because every plausible bug in a coder makes it emit too few bits rather than too many.

A code with a Kraft sum of 1.02 compresses better than any correct code and does not decode. A rolling model that updates before coding rather than after compresses better and does not decode. An arithmetic coder whose precision has silently overflowed compresses better and does not decode. Each of those would draw a figure showing an unusually good result, under a caption written as though it were true, and none of them would fail an assertion about bit counts.

The decoders are therefore not extras. They are the check, and they are why the coders in this file are written with both halves rather than with the half the figures use.

The floor moves, and that is the new thing

Bits per symbol on uniform over 8 symbols, 16,384 symbolsThe dashed line is the zeroth-order entropy of this stream, 3.000 bits per symbol: the floor for any coder that gives each symbol a code and looks at nothing else. Huffman and arithmetic coding are both such coders and neither is under it. The dictionary coders are not, and on this stream they do not beat it either.bits per symbolhuffman3.0001.00× the floorarithmetic (adaptive)3.0021.00× the floorLZSS (window 4096)4.9521.65× the floorLZ784.6001.53× the floorH₀ = 3.00016,384 symbols · alphabet 8 · floor shown is H0 = 3.000 bits/symbolmodel: order 0 · Uniform over 8 symbolsbest here: 3.000 bits/symbol
Fig. 3 Eight symbols drawn uniformly. The entropy is 3.000 bits per symbol exactly, Huffman spends 3.000 exactly — the code is three bits for every symbol and there is nothing to improve — and arithmetic coding spends 3.002, losing very slightly to the flush bits at the end of the stream. Both dictionary coders do considerably worse than either, because there is no repetition for them to find.

On a uniform source the floor is 3.000 and Huffman hits it to four decimal places. On a skewed source the floor is 0.759 and Huffman spends 1.289 — 70% above it. Same coder, same guarantee, wildly different distance to the floor.

Bits per symbol on one symbol at 0.9, 16,384 symbolsThe dashed line is the zeroth-order entropy of this stream, 0.760 bits per symbol: the floor for any coder that gives each symbol a code and looks at nothing else. Huffman and arithmetic coding are both such coders and neither is under it. The dictionary coders are not, and on this stream they do not beat it either.bits per symbolhuffman1.2891.70× the floorarithmetic (adaptive)0.7631.00× the floorLZSS (window 4096)1.5262.01× the floorLZ781.4121.86× the floorH₀ = 0.76016,384 symbols · alphabet 8 · floor shown is H0 = 0.760 bits/symbolmodel: order 0 · One symbol at 0.9best here: 0.763 bits/symbol
Fig. 4 One symbol at probability 0.9 and seven sharing the rest. The entropy is 0.759 bits per symbol. Arithmetic coding spends 0.763. Huffman spends 1.289 — because the dominant symbol is worth 0.152 of a bit and the shortest word a prefix code has is one whole bit, so it is overcharged by a factor of nearly seven on nine symbols out of ten. This is the case the next essay is entirely about.

And on a source that is genuinely predictable but not skewed at all — an order-1 Markov chain whose symbols are uniform when taken one at a time — the floor as computed above is 3.000, Huffman spends 3.000, and a dictionary coder spends 1.567.

That last measurement is the one that breaks the framing, and it is worth stating flatly: the dictionary coder went to 1.567 bits per symbol on a stream whose entropy is 3.000, and Shannon was not violated. The entropy of 3.000 is the entropy of a model that looks at symbols one at a time, and the Markov source has all of its structure in its pairs. A model that looks at pairs assigns that stream an entropy of 0.936, and 1.567 is comfortably above it.

Bits per symbol on order-1 markov chain, 16,384 symbolsThe dashed line is the zeroth-order entropy of this stream, 3.000 bits per symbol: the floor for any coder that gives each symbol a code and looks at nothing else. Huffman and arithmetic coding are both such coders and neither is under it. The dictionary coders are not, and LZSS (window 4096) and LZ78 finish below it — which is not a violation of anything, because the line is the floor for a different model than the one they use.bits per symbolhuffman3.0001.00× the floorarithmetic (adaptive)3.0021.00× the floorLZSS (window 4096)1.5670.52× the floorLZ782.0850.70× the floorH₀ = 3.00016,384 symbols · alphabet 8 · floor shown is H0 = 3.000 bits/symbolmodel: order 0 · Order-1 Markov chainbest here: 1.567 bits/symbol
Fig. 5 The stream that makes the point. Every symbol appears equally often, so the zeroth-order entropy is 3.000 and the two symbol coders spend exactly that. The stream is nonetheless almost completely predictable — each symbol is the previous one plus one, 85% of the time — and a coder with any memory at all finds it. Nothing about the data distinguishes this figure from the uniform one above under the model the dashed line belongs to.
Bits per symbol on order-1 markov chain, 16,384 symbolsThe dashed line is the zeroth-order entropy of this stream, 3.000 bits per symbol: the floor for any coder that gives each symbol a code and looks at nothing else. Huffman and arithmetic coding are both such coders and neither is under it. The dictionary coders are not, and LZSS (window 4096) and LZ78 finish below it — which is not a violation of anything, because the line is the floor for a different model than the one they use.bits per symbolhuffman3.0003.17× the floorarithmetic (adaptive)3.0023.18× the floorLZSS (window 4096)1.5671.66× the floorLZ782.0852.21× the floorH₀ = 3.00016,384 symbols · alphabet 8 · floor shown is H1 = 0.945 bits/symbolmodel: order 1 · Order-1 Markov chainbest here: 1.567 bits/symbol
Fig. 6 The same stream, the same four measurements, and one thing changed: the floor is now H1H_1 rather than H0H_0 — 0.936 bits per symbol instead of 3.000. Every bar is where it was. The line moved, and with it every “×the floor” figure in the right-hand column. This is the entire difference between this floor and the two the site had before it.

Which is why every plate here names its model

The convention this field adopts is the one the external-memory field adopted for BB and MM, and for a stronger reason. Those parameters change what a number means. This one changes what the floor is.

“This stream compresses to 2.1 bits per symbol against a floor of 3.9” is a different claim under a zeroth-order model and a third-order one, and both are true of the same stream. So every figure in this family prints the order its floor came from, and a quoted entropy without a stated model is not a quantity.

That has a consequence worth stating as bluntly as it deserves. A compressor that beats the entropy floor has not beaten Shannon. It has used a better model. Every result in this field is of that form, and the discipline is saying which model a floor belongs to.

The header, the learning, and where they tradeTwo arithmetic coders on the same source, differing only in where the model comes from. The static one measures the stream, sends the frequencies as a header, and codes at the floor thereafter. The adaptive one sends nothing and starts from a uniform model, so its early symbols cost far more than they are worth. At 64 symbols the header dominates; the adaptive coder stays ahead across this whole range.642561k4k16k64ksymbols codedbits per symbol0.02.75.4Static, with a headerAdaptive, no headerThe floor, H₀model: order 0 · Order-1 Markov chainheader 2.00 b/sym at n = 64, 0.0020 at 65,536
Fig. 7 A cost none of the bars above has been charged. Every floor on this page is computed from a stream’s own frequencies, which assumes the decoder has them — and it does not. Sending them costs a header, learning them costs the early symbols, and both fall away as the stream lengthens. On eight symbols the whole effect has amortised by a few thousand of them; on a wider alphabet it takes longer.

The relationship to the floor the site already had

The floor when the values repeat computes an entropy already, and it is important that it is not this one.

Take 256 values drawn from eight distinct ones. That essay computes 1,684 bits of permutation entropy — the floor for sorting them as though they were all distinct — and 738.7 bits of multiset entropy, which is the floor that actually applies to sorting them, and finds merge sort 2.3 times above it.

The same 256 values have a zeroth-order symbol entropy of 764.0 bits, and a Huffman coder spends 768 bits reproducing them.

Two numbers, 738.7 and 764.0, from the same data, and they are floors on different resources. The first bounds the comparisons needed to put the values in order. The second bounds the bits needed to reproduce the sequence. They are close because both are counting roughly the same information, and they are not equal because ordering a multiset is a slightly easier job than reproducing it — the sort does not need to know which of the identical values went where, and the coder does.

No compressor makes everything smaller

There is a fourth floor available here, it needs no model at all, and it is the one that makes the model-dependence of the third floor tolerable rather than alarming.

A compressor is an injective map from inputs to outputs — injective because it must decode. There are 2n2^n strings of exactly nn bits and fewer than 2nk+12^{n-k+1} strings of at most nkn-k bits, so at most one in 2k12^{k-1} inputs of a given length can come out kk bits shorter. Fewer than one in five hundred can be shortened by ten bits; fewer than one in a million by twenty.

That is a counting argument of exactly the kind this collection’s floors are made of, it applies to every compressor that has ever been written or ever will be, and it says something the entropy floor does not: compression is not a property of an algorithm, it is a transfer. Every input a compressor shortens is paid for by inputs it lengthens, and the reason a compressor is useful is that the files anybody wants to store are a vanishing subset of all files.

Set that beside the section above and the two fit together exactly. The entropy floor moves with the model because a model is a claim about which inputs are likely, and the counting floor says that any such claim must be a claim about a small set. A model that put appreciable probability on every string would predict nothing and its entropy would be the full nn bits; a model that puts almost all of its probability on a tiny set can have an entropy near zero, and it is wrong about everything outside that set — where the compressor duly makes the file bigger.

So “this compressor beat the entropy floor” and “this compressor makes some files bigger” are the same statement about the same choice. The dictionary coders in the plates above are not smarter than the symbol coders. They have bet on repetition, they win on streams that repeat, and on a stream of independent uniform symbols they spend more than the three bits a fixed-length code would — which the uniform plate shows without comment.

A floor on an average, and a code chosen first

One more clause hides in the theorem and it is the one most often dropped when a compression figure is quoted about a particular file.

The entropy bounds the expected length over the distribution, for a code fixed in advance. It says nothing whatever about any individual message. Any given string of a million bits can be coded in one bit, by a code that maps that string to 1 and everything else to something longer — and such a code is perfectly decodable and perfectly useless.

Which means a compression result is only a result if the decoder is counted. A program that reproduces a specific file and contains that file is a compressor achieving zero bits of output, and the only thing wrong with it is an accounting boundary drawn in the wrong place. Every claim about compressing one file is a claim about the pair (output, decoder), and where the line falls between them is a choice somebody made.

This collection’s coders are on the right side of that line by construction rather than by promise: each is a general procedure that codes any stream over its alphabet, the decoder is the same procedure run backwards, and neither holds anything about the streams the plates are drawn on. The learning cost plate is the visible edge of the same accounting — a coder that must transmit or learn its model pays for the model, and a floor computed from a stream’s own frequencies has quietly assumed the model arrived free.

The honest form of every number in this essay is therefore conditional twice over: bits per symbol, under a stated model, with the model’s own cost measured separately and shown to amortise. Both conditions are printed on the plates, and neither is in the phrase “compresses to 2.1 bits per symbol”.

What the counter is for

Three floors now, and the third has a property neither of the others has.

log2(n!)\log_2(n!) is fixed by nn. The membership bound is fixed by nn and ε\varepsilon. The entropy floor is fixed by a choice, and every choice gives a correct answer.

That could be read as making it weaker, and it is the reverse: it is what makes the field productive. A floor that cannot move gives an algorithm designer one number to approach and nothing else to do. A floor that moves with the model turns the whole problem into a different one — not how does a coder get closer to the floor, which arithmetic coding settled to within a hundredth of a bit, but what model puts the floor lower.

Every essay in the rest of this field is an answer to the second question. Contexts, dictionaries and transforms are three different answers, they are measured against three different floors, and each of them is correct about a stream that has not changed at all.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

What links here

Every essay whose body links to this one.

The objects this essay names

Each one links to every other essay that touches it.

Arithmetic codingBits per symbolCost modelEntropyHuffman codingInformation-theoretic boundKraft inequalityPrefix codeShannon bound