skip to content

questions

15

A compression tool claims it makes every possible input file smaller — why is that claim impossible?

level: juniorimportance: must knowfreq 68%

answer

  1. reversible means one-to-one
  2. count the outputs, not the files
  3. 2^n inputs, one fewer short strings
  4. pigeonhole: some input cannot shrink
  5. shrink one, expand another

basics

~20 s

Lossless compression must be reversible, so distinct inputs need distinct outputs. There are more n-bit inputs than shorter strings, so no map can shrink them all; a scheme that shrinks one input must expand another.

solid answer

~40 s

Lossless means `decompress(compress(x))` returns `x` for every input, which forces `compress` to be injective: two different inputs can never share an output, or the decompressor would not know which original to return. Now count. There are `2^n` inputs of exactly `n` bits, but only `2^n - 1` strings shorter than `n` bits in total, adding up all lengths from `0` to `n-1`. Pigeonhole: those inputs cannot all land on distinct shorter outputs, so at least one of them does not shrink. Stronger still, if the scheme does shrink some input, then some other input must come out strictly longer, because the short outputs it consumed are no longer available to the shorter inputs that would otherwise use them. The honest claim is always 'shrinks the files you actually have', never 'shrinks everything'.

go deeper

for a junior

Recall that lossless means exactly reversible and that reversibility forces different inputs to different outputs. From there the one-line count of inputs against shorter strings is the whole answer.

for a middle

Do the count out loud: 2^n inputs of length n against 2^n - 1 strings shorter than n, then explain why a scheme that shrinks one input must expand some other one.

for a senior

Use it to close a claim in review without benchmarking: ask what the scheme does with the inputs that do not shrink, and what the format emits for them.

for a principal

Treat 'compresses anything' in a design or procurement claim as a category error, and judge proposals on the input distribution they actually target and the growth bound they offer.

## What lossless demands of the map A compression scheme is a pair of procedures, `compress` and `decompress`, over finite bit strings. Calling it **lossless** is a promise about every possible input, not about a test corpus: for any input `x`, `decompress(compress(x))` gives back `x` bit for bit. That one promise fixes a structural property of `compress` before any code is written. - If two different inputs `x` and `y` ever produced the same output, the decompressor would be handed one string and asked to return two different originals. It cannot. - So `compress` must be **injective**, one-to-one: different in, different out, always. - Injectivity is not an implementation choice that a cleverer author could avoid. It is what the word lossless means. Everything after that is counting. ## The count Fix a length `n` and ask what can happen to the inputs of exactly that length. 1. There are exactly `2^n` bit strings of length `n`, and every one of them is a legal input. 2. The strings strictly shorter than `n` bits are those of length `0, 1, ..., n-1`. Summing the powers of two gives `1 + 2 + 4 + ... + 2^(n-1) = 2^n - 1`. 3. So the outputs that would count as a win are one short of the inputs that need them, before a single short slot is given away to a shorter input that also wants one. 4. By the **pigeonhole principle**, an injective map cannot place `2^n` items into `2^n - 1` slots. At least one input of length `n` comes out no shorter than it went in. | Input length | Inputs of that length | Strings strictly shorter | Slots short by | |---|---|---|---| | 8 bits | 256 | 255 | 1 | | 16 bits | 65,536 | 65,535 | 1 | | n bits | 2^n | 2^n - 1 | 1 | The margin is always exactly one, which makes the bound look weightless. It is not, because of what comes next. ## Shrinking one input forces another to grow Suppose the scheme shrinks at least one input `x0`, and suppose, for contradiction, that nothing ever grows, so every output is at most as long as its input. Let `n` be the length of `x0`. Every string shorter than `n` bits then maps to a string shorter than `n` bits, and an injective map from that finite set of `2^n - 1` strings into itself is a permutation of it: the short inputs already occupy every short slot. But `x0` also maps into that same set, so its output collides with the output of some shorter string, contradicting injectivity. Therefore: - A scheme that never grows anything is a scheme that never shrinks anything, a relabelling at best. - The moment it wins on one input it loses on another. This is a conservation statement, not a warning about rare edge cases. - **Which** inputs pay is the designer's choice, and every real format makes the same one: lose on inputs that look like uniform noise, win on the structured inputs people actually store. ## The honest version of the claim - *'Shrinks event logs by eight to ten times'* is a claim about a corpus. Checkable, usually true, and no counting argument touches it. - *'Shrinks any file'* is a claim about the whole input space. False by counting, and no benchmark is needed to refute it. - *'Shrinks its own output again'* is the recursive form of the same false claim. Each pass is injective, so iterating would funnel every distinct input into a handful of short strings that cannot be told apart. - *'Never grows a file'* is achievable only by never shrinking one. What a real format offers instead is a **bound** on growth: emit the original bytes when coding them would be bigger, and pay only a small block header. ## Two objections worth answering - *'Everything in my archive folder shrank.'* Real files occupy a vanishingly small, highly structured corner of the space of all bit strings, and a coder is built for that corner. The bound says no scheme is universal; it never claimed no scheme is useful. - *'Then how can a lossy coder shrink everything?'* Because a lossy coder is deliberately **not** injective: many inputs share an output and the decoder returns an approximation. The obstruction here is exact reversibility, so giving that up removes it. - *'Could a dictionary shared by both sides fix it?'* No. Fixing a shared dictionary changes which inputs are cheap; the map over all inputs stays injective, and the count is unchanged. ## Why an interviewer asks it The payoff is not the theorem, it is the speed. An engineer meets this whenever a pipeline compresses something that was already compressed, whenever a proposal promises a fixed ratio on arbitrary user data, and whenever someone suggests a second pass to squeeze out more. The counting argument settles all three in one sentence, in a review, without a benchmark: ask what the scheme does with the inputs that do not shrink.

  • Someone proposes compressing the compressed output again and again until the archive is one byte. Refute it in a line.
    Each pass is injective, so repeating it would eventually funnel all `2^n` distinct inputs into a handful of short strings, and no decoder could tell them apart. In practice the second pass sees near-uniform bytes, finds no skew and no repeats, and returns the same payload plus another layer of framing.
  • Does the counting argument also limit how badly a scheme can expand an input?
    No. It forbids universal shrinkage but says nothing about the size of the loss; a naive scheme could double its input. Formats impose that limit themselves by emitting a block's original bytes whenever coding them would be larger, so the worst case becomes the small header on that block.
  • Why does the argument not rule out a lossy coder that shrinks every input?
    Lossy coders are not injective: many inputs map on purpose to the same output, so the pigeonhole obstruction disappears. The cost moves to the reconstruction, which is an approximation rather than the original, and the relevant bound becomes rate against fidelity instead of a count.

A cloakroom with 256 coats and 255 numbered hooks. You can hang the coats however you like, but one of them has no hook of its own, and you find out only when everybody tries to collect the right coat back.

saying these in an interview costs you the question

  • Says a smarter algorithm will eventually shrink every possible file
  • Thinks the limit comes from CPU or memory rather than counting
  • Believes feeding the output back in keeps shrinking it
  • Says the same counting bound also stops lossy coders shrinking everything
  • Offers a folder where every file shrank as a counterexample
open as a page

Why does an archival tier re-compressing already-compressed or encrypted blocks get output slightly larger than the input?

level: middleimportance: must knowfreq 46%

basics

~20 s

Already-compressed and encrypted blocks have no repeated sequences and a near-uniform byte distribution, so the coder finds nothing to exploit and emits at least as much as it read, plus headers — a few bytes of growth per block.

open as a page

Which test tells you whether a prefix code exists with codeword lengths of 1, 2, 3, 3 and 3 bits?

level: middleimportance: must knowfreq 48%

basics

~20 s

Kraft's inequality: sum two to the minus each codeword length and compare with 1. For 1, 2, 3, 3 and 3 that sum is 1.125, above the budget, so no prefix code has those lengths.

open as a page

A binary envelope packs variable-length field tags back to back with no separator bits - why must those tag codewords be prefix-free?

level: middleimportance: must knowfreq 62%

basics

~20 s

Prefix-free means no codeword is the opening of another, so a decoder reading bit by bit knows a symbol has ended the instant it recognises one. Without that property it must look ahead or carry explicit lengths and separators.

open as a page

What guarantee does a stored-raw fallback block give a lossless container, and what does it cost?

level: middleimportance: should knowfreq 37%

basics

~20 s

A stored-raw block lets the writer emit a block's original bytes whenever coding them would be bigger, so worst-case expansion is capped by that block's small header rather than by the coder's behaviour — usually a small fraction of a percent.

open as a page

If Kolmogorov complexity is the length of the shortest program printing a file, why is a compressed size only an upper bound?

level: middleimportance: should knowfreq 40%

basics

~20 s

Kolmogorov complexity is the length of the shortest program that outputs a string. A decoder plus its payload is one such program, so it bounds the complexity from above. Nothing a coder fails to do bounds it from below.

open as a page

An archival tier's compressed copy of a corpus came out larger than the originals — what does that tell you?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A corpus that grows says its blocks are already compressed or encrypted: nearly every block falls back to a raw copy and pays a header, so the total rises by about one header per block. No different coder fixes that.

open as a page

Under Kolmogorov complexity, is a gigabyte grown from a 200-byte seed random, given that no coder shrinks it?

level: seniorimportance: should knowfreq 34%

basics

~20 s

No. Its Kolmogorov complexity is at most the seed plus the generator, a few hundred bytes, so a very short description exists. The coders tried failed to find it, which is not the same as no description existing.

open as a page

Why can no program compute the Kolmogorov complexity of an arbitrary input string?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Because such a function lets you write a short program that hunts down and prints the first string it certifies as needing a long description, describing that string in far fewer bits than the certificate claims. The contradiction rules the function out.

open as a page

The source-coding theorem bounds an optimal prefix code's expected length by H <= L < H+1, where H is the source's Shannon entropy in bits - where does that extra bit come from?

level: seniorimportance: should knowfreq 40%

basics

~20 s

From rounding. The ideal length for a symbol of probability p is the generally fractional value log2(1/p), but a codeword is a whole number of bits, so each symbol pays up to one bit of rounding - and at least one bit even when its ideal length is far below that.

open as a page

Kolmogorov complexity cannot be measured, so what do you put in a build gate meant to reject test fixtures carrying too little content?

level: principalimportance: should knowfreq 25%

basics

~20 s

Gate on the provable direction only: a fixture that compresses sharply demonstrably has a short description, so alarm on that. Never let the gate certify the opposite, and pair it with recorded provenance for the fixtures it passes.

open as a page

You own a binary envelope's tag code and new field types keep arriving - how much of the Kraft budget do you leave unspent?

level: principalimportance: should knowfreq 28%

basics

~20 s

Enough to add tags without lengthening deployed codewords, and no more. A code whose Kraft sum is exactly 1 is closed: the next tag forces an existing codeword to grow. Reserving a leaf at depth d costs 2^-d of the budget.

open as a page

For a lossless scheme, what fraction of fixed-length inputs can shrink by at least 10 bits?

level: middleimportance: nice to knowfreq 21%

basics

~20 s

Fewer than one input in 512. Only 2^(n-9) - 1 strings are short enough to be a ten-bit win, against 2^n inputs of length n, so under 1 in 2^9 qualify; in general, under 1 in 2^(k-1) for k bits.

open as a page

Kolmogorov complexity is defined relative to a chosen description language, so why is the measure not arbitrary?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

Because any two universal description languages can interpret each other. Writing one interpreter in the other is a fixed cost, so their complexity values for every string differ by at most that one constant, independent of the string.

open as a page

A proposed tag code is uniquely decodable but not prefix-free - what does allowing that lookahead actually buy you?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

Nothing in size. The Kraft-McMillan result says every uniquely decodable code satisfies the same sum bound as a prefix code, so a prefix code exists with exactly the same codeword lengths - the lookahead costs buffering and decode complexity for zero saved bits.

open as a page