Shannon measures the information of a source. Kolmogorov measures the information of a single string: the length of its shortest program.
K(x) = shortest program for x. The invariance theorem makes it well-defined.
Most strings cannot be compressed. K itself cannot be computed.
Random = incompressible. Martin-Löf, Chaitin's Ω, incompleteness.
MDL, Solomonoff induction, and NCD clustering with plain zlib and lzma.
Every Python snippet in this deck runs as shown. All outputs are copied from real runs with Python 3.11+ (standard library only).
| Year | Event |
|---|---|
| 1948 | Shannon: entropy of a random source. |
| 1960–64 | Ray Solomonoff: a universal prior for induction, built from program lengths. |
| 1965 | Andrey Kolmogorov: "Three approaches to the quantitative definition of information". |
| 1966–69 | Gregory Chaitin, then 18–19, finds the same idea on his own. |
| 1966 | Per Martin-Löf defines random infinite sequences by tests. |
| 1974–75 | Levin, Gács and Chaitin: prefix-free K, coding theorem, Ω. |
| 1978 | Rissanen: Minimum Description Length (MDL). |
| 1988 | Bennett: logical depth. |
| 1993 | Li & Vitányi's textbook. It is still the standard reference. |
| 2004–05 | Cilibrasi & Vitányi: NCD. Clustering by compression. |
It is also called algorithmic information, algorithmic entropy, program-size complexity, or descriptive complexity. Some write C(x) for the plain version and K(x) for the prefix-free version. We do the same.
Toss a fair coin 32 times. Each string below has the same probability, 2−32:
A: 01010101010101010101010101010101 B: 00000000000000000000000000000000 C: 10011101001011100010110111010010
Yet everyone says C "looks random" and A and B do not. Probability cannot explain this. Every single string is equally unlikely.
A has a short description: "print 01 sixteen times". B does too. For C, the shortest description seems to be C itself. A string is random when it has no description much shorter than itself.
prog = 'print("ab" * 50000)'
print(len(prog), "chars of program for", len(samples["'ab' repeated"]), "bytes of output")
19 chars of program for 100000 bytes of output
Python is a fine universal machine for intuition. The theory uses a fixed universal Turing machine instead. The invariance theorem says the choice barely matters.
A handy trick. To describe x, name a set S that holds it. Then give x's position (index) inside S.
cost ≈ K(S) + log2|S| bits
The set is the pattern. The index is the leftover noise. Here n = 1000, and c is a small fixed cost for the decoder.
| String x (1000 bits) | How we describe it | About |
|---|---|---|
000…0 | "n zeros", just give n | log 1000 ≈ 10 + c |
0101…01 | "01 repeated 500 times" | log 500 ≈ 9 + c |
| exactly 3 ones | n, then which of C(1000,3) sets | 27.3 + O(log n) |
| exactly 100 ones | n, 100, then index in C(1000,100) | 464.4 + O(log n) |
| first 1000 bits of √2 | a fixed √2 program, plus n | c + 10 |
| 1000 fair coin flips | no pattern, so print it | ≈ 1000 |
Compare Shannon: 1000·H(0.1) = 469.0 bits. Same answer, up to O(log n). We will prove this link later (K vs H).
"√2 looks random, so its K is high." No. Its bits pass many statistical tests. But a short program prints them. K asks for any pattern at all, not just the ones a test looks for.
0110 repeated 250 times?Give the 4-bit block and the count 250. That is about 4 + log 250 ≈ 12 bits, plus c.
Plain complexity. Let M be a partial computable function from binary strings to binary strings (a "machine"). Then
CM(x) = min { |p| : M(p) = x }
with min ∅ = ∞. Here p is a description (program) of x.
Conditional complexity. Give the machine side information y for free:
CM(x | y) = min { |p| : M(p, y) = x }
So C(x) = C(x | ε), where ε is the empty string.
Universal machine U: for every machine M, some string 〈M〉 exists with U(〈M〉 p) = M(p) for all p.
Encode them as strings first. A number n is its binary form. A pair is a self-delimiting encoding of both parts. Then K(n), K(x, y) and K(graph) all make sense.
| x | C(x) about |
|---|---|
| 0n (n zeros) | C(n) ≤ log n |
| 0n with n = 22k | log k, tiny |
| first n digits of π | log n |
| n fair coin flips | n, almost surely |
| x x (a copy) | C(x) |
Logs are base 2. "± c" or O(1) means a constant that depends on U but not on x.
Theorem (Solomonoff, Kolmogorov, Chaitin). Let U be universal. For every machine M, a constant cM exists with
CU(x) ≤ CM(x) + cM for all x.
So if U and V are both universal, |CU(x) − CV(x)| ≤ cUV.
One detail: U must know where 〈M〉 ends and p starts. So the machine codes 〈M〉 are chosen prefix-free.
The constant can be large. For one short string, K depends a lot on the machine. The theory is about long strings and asymptotics.
Upper bound. A constant c exists with C(x) ≤ |x| + c for all x.
Proof. Let M be the identity machine, M(p) = p. Then CM(x) = |x|. Apply invariance. ∎ In Python terms: print("...x...").
Computable maps don't add information. For computable f:
C(f(x)) ≤ C(x) + cf
Proof. Run the shortest program for x, then apply f. ∎ Sorting, encrypting with a fixed key, or copying never adds more than a constant.
Numbers. C(n) ≤ log n + c. And C(n) ≥ log n for most n.
| Fact | Bound (± O(1)) |
|---|---|
| copy | C(x x) ≤ C(x) |
| reverse, complement | C(xR) = C(x) |
| length | C(0n) = C(n) |
| pairs (plain C) | C(x, y) ≤ C(x) + C(y) + 2 log C(x) |
| pairs (prefix K) | K(x, y) ≤ K(x) + K(y) |
| prefix vs plain | C(x) ≤ K(x) ≤ C(x) + 2 log C(x) |
| prefix upper | K(x) ≤ |x| + 2 log |x| |
To glue two programs together, the machine must know where the first one ends. Writing its length costs about log bits, doubled to make it self-delimiting. Prefix-free K builds this in (see the Kraft slide).
C is not monotone in prefixes. A long string can be simpler than one of its prefixes. For example, 0n with n = 22k is simpler than most shorter runs of zeros.
Theorem. For every n and c, fewer than 2n−c strings of length n have C(x) < n − c. So at least 2n(1 − 2−c) strings are c-incompressible.
With c = 0: at least one string of every length has C(x) ≥ n. That string is truly incompressible.
def fraction_compressible(n, c):
"""Fraction of n-bit strings that have a description of < n - c bits."""
programs = 2 ** (n - c) - 1 # all strings of length 0 .. n-c-1
return programs / 2 ** n
for c in [1, 2, 8, 10, 20]:
print(f"save more than {c:>2} bits: at most {fraction_compressible(64, c):.7f} of 64-bit strings")
save more than 1 bits: at most 0.5000000 of 64-bit strings save more than 2 bits: at most 0.2500000 of 64-bit strings save more than 8 bits: at most 0.0039062 of 64-bit strings save more than 10 bits: at most 0.0009766 of 64-bit strings save more than 20 bits: at most 0.0000010 of 64-bit strings
No lossless compressor can shrink all files. If it shrinks some, it must grow others. Fewer than 1 file in 1000 can shrink by more than 10 bits. zip works only because real files are far from random.
Recipe. Pick an incompressible object. Assume the claim fails. Use the failure to describe the object in too few bits. Contradiction.
Theorem. There are infinitely many primes.
Proof. Suppose only p1, …, pk exist. Then every n = p1e1 … pkek. Each ei ≤ log n, so it fits in log log n bits. Then n is described by its k exponents:
C(n) ≤ 2k log log n + c
Now pick an incompressible n with C(n) ≥ log n. For large n, log n > 2k log log n + c. Contradiction. ∎
It replaces "for a random input, with high probability" with "for this incompressible input, always". One fixed object has every typical property at once. The counting slide says such objects exist.
Claim. No DFA accepts L = { 0n1n : n ≥ 0 }.
Proof, step by step.
The idea in one line: a DFA's memory is a constant number of bits. The language needs it to remember a number that is not a constant.
Take a DFA A for it. Pick x of length n with C(x) ≥ n. Run A on x and note the state s. From s, the only length-n string y that gets accepted is y = x. So A, s and n rebuild x: C(x) ≤ 2 log n + c. That is less than n for large n. Contradiction.
Compare the pumping lemma. It proves the same thing, but you must juggle "for all splits". The incompressibility proof has no case analysis.
"The smallest number not definable in under sixty letters." That phrase has only 48 letters. So it defines the number in under sixty. Contradiction.
Theorem (Kolmogorov, Chaitin). No program computes K(x) for all x. Even any computable lower bound is bounded by a constant.
from itertools import product
def first_hard(k):
"""First bit-string (by length, then lexicographic) whose zlib size is >= k bytes."""
for n in range(1, 64):
for bits in product(b"01", repeat=n):
s = bytes(bits)
if len(zlib.compress(s, 9)) >= k:
return s
print(first_hard(20).decode())
00001011100110
This is no paradox. The program above is short, and it describes its output. That just proves zlib size is not K. zlib calls a 14-character bit string "20 bytes complex".
K is upper semi-computable. Run all programs in parallel (dovetailing). Each time one outputs x, lower your estimate. The estimates reach K(x) at some point. But you can never know when.
Theorem. Computing K and solving the halting problem are Turing-equivalent. Each one can be computed using the other as an oracle.
For L = 0, 1, 2, …: list every program of length L. Ask the oracle which ones halt. Run just those. The first L where some program outputs x is K(x).
Fix n and let m = n + c. Using K, list every string with K(x) < m (a finite set). Dovetail all programs until each of those strings has been printed. Call that time T. Suppose some program p with |p| < n halts at time t > T. Then "run p to learn t; run every program for t steps; print the first string of length m not yet printed" is a program of length n + O(1). Its output has K ≥ m = n + c. For large c that is a contradiction. So every halting program shorter than n halts by time T. Run them all for T steps and you know which ones halt.
"K is uncomputable, so it is useless." No. We can't compute it, but we can prove things with it. And we can bound it from above with any compressor.
0110 has a 1-bit program. K speaks about long strings and growth rates.Prefix-free set: no word is a proper prefix of another. A prefix machine has a prefix-free domain. Then a program "knows where it ends", like a Huffman code.
K(x) = min { |p| : Uprefix(p) = x }
Kraft inequality. If S is prefix-free, then ∑w∈S 2−|w| ≤ 1. Conversely, any lengths with this sum ≤ 1 can be realized by some prefix-free code.
Proof. Word w owns the interval [0.w, 0.w + 2−|w|) in [0,1). Prefix-free means these intervals don't overlap. So their total length is at most 1. ∎
def elias_gamma(n: int) -> str:
b = bin(n)[2:]
return "0" * (len(b) - 1) + b
def elias_delta(n: int) -> str:
b = bin(n)[2:]
return elias_gamma(len(b)) + b[1:]
for n in [1, 2, 5, 17, 1000]:
print(f"{n:>5} gamma={elias_gamma(n):<20} delta={elias_delta(n)}")
codes = [elias_gamma(n) for n in range(1, 2000)]
print("prefix-free:", is_prefix_free(codes),
" Kraft sum:", round(sum(2 ** -len(w) for w in codes), 6))
1 gamma=1 delta=1
2 gamma=010 delta=0100
5 gamma=00101 delta=01101
17 gamma=000010001 delta=001010001
1000 gamma=0000000001111101000 delta=0001010111101000
prefix-free: True Kraft sum: 0.999489
Gamma costs 2 log n + 1 bits. Delta costs log n + 2 log log n + O(1). is_prefix_free sorts the words and checks neighbours. The Kraft sum tends to 1 as more words are added.
Write n in binary. If it has L bits, put L − 1 zeros in front.
| n | binary | gamma(n) | 2−len |
|---|---|---|---|
| 1 | 1 | 1 | 1/2 |
| 2, 3 | 10, 11 | 010, 011 | 1/8 each |
| 4–7 | 100–111 | 00100 … 00111 | 1/32 each |
| 13 | 1101 | 0001101 | 1/128 |
Kraft sum for 1..7: 1/2 + 2/8 + 4/32 = 0.875 ≤ 1. Good.
13 = 1101 has 4 bits. So write gamma(4) = 00100, then the bits after the leading 1: 101. Result 00100101 (8 bits). Gamma used 7 here, but delta wins for large n.
001010100111101 = 5.10 = 2.11 = 3.1 = 1.No commas needed. Each word tells you where it ends. That is what "prefix-free" buys.
{0, 01, 11} has sum 1/2+1/4+1/4 = 1. But 0 is a prefix of 01, so it is not prefix-free. The theorem only says some code with lengths 1, 2, 2 exists: {0, 10, 11}.9 = 1001, 4 bits. So gamma(9) = 0001001, 7 bits = 2⌊log 9⌋ + 1.
Algorithmic probability. Feed a prefix machine U fair coin flips. The chance it outputs x is
m(x) = ∑p : U(p) = x 2−|p|
Kraft gives ∑x m(x) ≤ 1, since some programs never halt. It is a semimeasure.
Coding theorem. K(x) = −log m(x) + O(1).
Sketch. The ≥ side holds because the shortest program alone adds 2−K(x) to m(x). For ≤, approximate m from below. Then use the Kraft–Chaitin construction to give each x a codeword of length −log m(x) + 2. That code is a prefix machine, so invariance applies.
| Shannon | Kolmogorov |
|---|---|
| code length −log P(x) | K(x) = −log m(x) |
| needs to know P | one prior m for everything |
| Kraft for codes | Kraft for programs |
Levin calls m the "a priori probability". Solomonoff used the same idea for prediction (see later).
Theorem. Let P be a computable distribution on strings. Then
0 ≤ ∑x P(x) K(x) − H(P) ≤ K(P) + O(1)
For n i.i.d. draws, divide by n. The K(P)/n term vanishes. Per symbol, expected K equals the entropy rate.
def H(p): return -p * math.log2(p) - (1 - p) * math.log2(1 - p)
print(f"{'p':>5}{'H(p)':>8}{'lzma bits/bit':>15}")
for p in [0.5, 0.25, 0.1, 0.01]:
data = bernoulli_bytes(p, 400_000, seed=7)
rate = 8 * len(lzma.compress(data, preset=9)) / 400_000
print(f"{p:>5}{H(p):>8.3f}{rate:>15.3f}")
p H(p) lzma bits/bit 0.5 1.000 1.001 0.25 0.811 0.838 0.1 0.469 0.502 0.01 0.081 0.107
bernoulli_bytes(p, n, seed) packs n biased coin flips into bytes. lzma is a general tool, not tuned for this source. It still lands within about 0.03 bits of the entropy.
H describes the average. One sample 0n from a fair coin has K ≈ log n, far below n. K answers questions about single objects that H cannot.
Conditional K: K(x | y) is the shortest program that outputs x when given y. It measures how much new information x has beyond y.
Symmetry of information (Kolmogorov–Levin 1968, Gács 1974, Chaitin 1975).
K(x, y) = K(x) + K(y | x, K(x)) + O(1)
So the algorithmic mutual information
I(x : y) = K(y) − K(y | x*)
is symmetric: I(x : y) = I(y : x) + O(1). Here x* is the shortest program for x.
Compare Shannon: H(X,Y) = H(X) + H(Y|X). The algorithmic version is the same law, for single objects, with the small K(x) correction.
def C_given(x: bytes, y: bytes) -> int:
"""C(x | y) ~ C(y + x) - C(y)."""
return C(y + x) - C(y)
x = docs["dnaA'"]
print("C(x) =", C(x), " C(x | dnaA) =", C_given(x, docs["dnaA"]),
" C(x | dnaB) =", C_given(x, docs["dnaB"]))
C(x) = 1178 C(x | dnaA) = 631 C(x | dnaB) = 1109
dnaA' is dnaA with 10% of its letters changed.dnaA nearly halves the cost. What is left is mostly the mutations.dnaB barely helps.C(x) is the smallest of zlib, lzma and bz2 output, in bytes. The docs are built on the NCD slide. The trick "compress y then x" is the idea behind NCD.
Information distance (Bennett, Gács, Li, Vitányi, Zurek 1998): the shortest program that turns x into y and y into x.
E(x, y) = max { K(x | y), K(y | x) } + O(log)
Universality. E is a metric (up to small terms). For every "reasonable" computable distance D, E(x,y) ≤ D(x,y) + O(1). If two objects are close in any computable sense, they are close in E.
Normalized (NID), scaled to [0, 1]:
NID(x,y) = max{K(x|y), K(y|x)} / max{K(x), K(y)}
NCD: replace K with a real compressor Z:
NCD(x,y) = (Z(xy) − min{Z(x),Z(y)}) / max{Z(x),Z(y)}
Real uses: mtDNA trees of mammals, language family trees from the UN Human Rights text, music genres, plagiarism and malware families, and anomaly detection.
def Z(x: bytes) -> int:
return len(lzma.compress(x, preset=9 | lzma.PRESET_EXTREME))
def ncd(x: bytes, y: bytes) -> float:
zx, zy, zxy = Z(x), Z(y), Z(x + y)
return (zxy - min(zx, zy)) / max(zx, zy)
def mutate(s: bytes, rate: float, seed: int) -> bytes:
"""Replace a fraction `rate` of bytes with other bytes from s itself."""
r = random.Random(seed)
return bytes(r.choice(s) if r.random() < rate else c for c in s)
r = random.Random(1)
dna_a = bytes(r.choice(b"ACGT") for _ in range(4000))
dna_b = bytes(r.choice(b"ACGT") for _ in range(4000))
eng = b" ".join(r.choice([b"it was the best of times", ...]) for _ in range(150))
code = b"\n".join(r.choice([b"def f(n):", b" if n < 2:", ...]) for _ in range(150))
docs = {"dnaA": dna_a, "dnaA'": mutate(dna_a, 0.10, 2),
"dnaB": dna_b, "dnaB'": mutate(dna_b, 0.10, 3),
"eng": eng, "eng'": mutate(eng, 0.10, 4),
"code": code, "code'": mutate(code, 0.10, 5)}
names = list(docs)
D = {(a, b): ncd(docs[a], docs[b]) for a in names for b in names if a != b}
Each eng and code list has 8 phrases or lines. The full lists are in the test file.
dnaA dnaA' dnaB dnaB' eng eng' code code' dnaA - 0.29 0.84 0.83 0.95 0.95 0.95 0.95 dnaA' 0.29 - 0.84 0.83 0.95 0.95 0.95 0.95 dnaB 0.84 0.84 - 0.28 0.95 0.95 0.95 0.95 dnaB' 0.84 0.84 0.25 - 0.95 0.95 0.95 0.95 eng 0.93 0.93 0.93 0.93 - 0.48 0.75 0.91 eng' 0.93 0.93 0.92 0.93 0.83 - 0.91 0.87 code 0.93 0.93 0.92 0.93 0.74 0.91 - 0.47 code' 0.93 0.93 0.92 0.93 0.90 0.86 0.81 -
NCD(x,y) = (Z(xy) − min{Z(x),Z(y)}) / max{Z(x),Z(y)}
Read it as: "How many new bytes does y add once x is known, compared to the bigger file?" Sizes are lzma bytes from the demo.
| Pair | Z(x) | Z(y) | Z(xy) | Work | NCD |
|---|---|---|---|---|---|
| dnaA, dnaA' | 1400 | 1400 | 1812 | (1812−1400)/1400 = 412/1400 | 0.29 |
| dnaA, eng | 1400 | 368 | 1692 | (1692−368)/1400 = 1324/1400 | 0.95 |
| eng, code | 368 | 372 | 648 | (648−368)/372 = 280/372 | 0.75 |
dnaA is 4000 random letters from ACGT. Each letter holds 2 bits, so the ideal size is 4000 × 2 / 8 = 1000 bytes. lzma gives 1400. The extra 400 bytes are overhead and model cost. So Z is only an upper bound on K, as promised.
(900 − 500) / 800 = 0.5. Knowing x leaves only 400 new bytes to pay for.
def single_link(names, D):
clusters = [[n] for n in names]
while len(clusters) > 1:
i, j = min(((i, j) for i in range(len(clusters))
for j in range(i + 1, len(clusters))),
key=lambda ij: min(D[a, b] for a in clusters[ij[0]]
for b in clusters[ij[1]]))
d = min(D[a, b] for a in clusters[i] for b in clusters[j])
print(f"merge {clusters[i]} + {clusters[j]} at {d:.2f}")
clusters[i] = clusters[i] + clusters[j]; del clusters[j]
single_link(names, D)
merge ['dnaB'] + ["dnaB'"] at 0.28 merge ['dnaA'] + ["dnaA'"] at 0.29 merge ['code'] + ["code'"] at 0.47 merge ['eng'] + ["eng'"] at 0.48 merge ['eng', "eng'"] + ['code', "code'"] at 0.75 merge ['dnaA', "dnaA'"] + ['dnaB', "dnaB'"] at 0.83 merge ['dnaA', "dnaA'", 'dnaB', "dnaB'"] + ['eng', "eng'", 'code', "code'"] at 0.95
eng joins code before any DNA. Both are ASCII with spaces and English words.What makes an infinite 0/1 sequence random? Frequency rules like "half zeros" are not enough. 0101010… passes that test. We want a sequence to pass every effective test.
Martin-Löf test (1966): a computable list of sets of sequences U1 ⊇ U2 ⊇ …. Each Um is a c.e. union of cylinders [w], with measure μ(Um) ≤ 2−m.
A sequence ω fails the test if ω ∈ ∩m Um. It is ML-random if it fails no test.
Levin–Schnorr theorem (1973). ω is ML-random if and only if
∃c ∀n: K(ω1 … ωn) ≥ n − c
"Random" (passes all tests) equals "incompressible" (every prefix). Two very different ideas give one class.
With plain C, no sequence has C(ω1..n) ≥ n − c for all n. Martin-Löf showed this "complexity dips" effect. Prefix-free K fixes it.
Randomness deficiency of a length-n string, with respect to the uniform distribution:
δ(x) = n − K(x | n)
For a general computable P: δP(x) = −log P(x) − K(x | P).
Fact. P(δP(x) ≥ k) ≤ 2−k. Large deficiency is rare, so it is evidence against P.
Proof. Strings with K(x|P) ≤ −log P(x) − k have total probability at most ∑ 2−K(x|P) − k ≤ 2−k by Kraft. ∎
So δ is a universal test statistic. A deficiency of 30 bits is like a p-value of 2−30. And it holds for every computable test at once, not just the one you picked.
def deficiency_bits(x: bytes) -> int:
"""n - C(x) in bits: a lower bound on the true deficiency n - K(x), up to O(1)."""
return 8 * len(x) - 8 * C(x)
tests = {
"fair coin": bernoulli_bytes(0.5, 80_000, seed=11),
"biased p=0.1": bernoulli_bytes(0.1, 80_000, seed=12),
"period 7": (b"\x5a\x13\x77\x00\xff\x42\x99" * 1500)[:10_000],
}
for name, x in tests.items():
print(f"{name:<14} n={8*len(x)} bits n - C(x) = {deficiency_bits(x):>6} bits")
fair coin n=80000 bits n - C(x) = -88 bits biased p=0.1 n=80000 bits n - C(x) = 36128 bits period 7 n=80000 bits n - C(x) = 79608 bits
Definition (Chaitin 1975). For a universal prefix machine U:
Ω = ∑p : U(p) halts 2−|p|
This is the chance that U halts when fed fair coin flips. By Kraft, 0 < Ω < 1.
Theorem. The first n bits of Ω decide halting for every program of length ≤ n.
Proof. Dovetail all programs. Add 2−|p| each time one halts, until the sum reaches 0.Ω1…Ωn. A program with |p| ≤ n that halts later would push the sum past Ω1..n + 2−n > Ω. That is impossible. So every program still running now never halts. ∎
Corollary. Ω is ML-random: K(Ω1..n) ≥ n − c.
Proof. From Ω1..n, find every halting program of length ≤ n and its output. Print a string that is not among those outputs. That string has K > n. We made it from Ω1..n, so n < K(Ω1..n) + c. ∎
Ω depends on U. Calude, Hertling, Khoussainov, Wang and Kučera–Slaman showed that the left-c.e. ML-random reals are exactly the Ωs of the different universal machines.
Theorem (Chaitin 1971). Let F be a sound, computably axiomatized theory (like ZFC). There is a constant LF such that F proves "K(x) > LF" for no specific string x.
Yet all but finitely many strings satisfy it.
| Gödel (1931) | Chaitin (1971) |
|---|---|
| the liar: "I am unprovable" | Berry: "the first string proven complex" |
| one strange sentence | almost all true sentences of a kind |
| needs self-reference | needs only counting and search |
In practice, LF is a few thousand bits for ZFC. You cannot prove that any specific megabyte file is incompressible, even though nearly all of them are.
"Chaitin says some strings have unknown K." It says more. A fixed theory can prove K(x) > L for no string at all, once L passes a constant. Yet almost every string has K that large.
{0, 10, 110, 111}?
Two-part MDL (Rissanen 1978). Choose the model M that minimizes
L(M) + L(D | M)
First pay to describe the model. Then pay to describe the data using it. A complex model makes L(D|M) small, but L(M) big.
Data: 20,000 bits from a source that repeats its last bit with probability 0.9. That is order-1 Markov. Which order k does MDL pick?
s = markov_source(20_000, seed=3)
print(f"{'k':>2}{'L(model)':>10}{'L(data|model)':>15}{'total':>10}")
best = None
for k in range(6):
m, d = two_part_bits(s, k)
print(f"{k:>2}{m:>10.0f}{d:>15.0f}{m + d:>10.0f}")
if best is None or m + d < best[1]: best = (k, m + d)
print("MDL picks k =", best[0])
k L(model) L(data|model) total 0 7 20000 20007 1 14 9307 9321 2 29 9304 9333 3 57 9299 9356 4 114 9293 9408 5 229 9279 9508 MDL picks k = 1
two_part_bits counts next bits per context. It charges the empirical entropy for the data and 2k · ½ log n for the model. Higher orders fit a little better, but not enough to pay for their parameters.
Universal prior on sequences (a monotone machine U):
M(x) = ∑p : U(p) starts with x 2−|p|
Predict with M(b | x) = M(xb) / M(x). Every program that fits the past gets a vote, weighted by 2−length.
Solomonoff's theorem (1978). Suppose the data comes from any computable measure μ. Then
∑t Eμ[(M(0|x<t) − μ(0|x<t))2] ≤ (ln 2 / 2) · K(μ)
The total error is finite. So predictions converge to the truth fast, for every computable world.
It is uncomputable, like K. AIXI (Hutter) adds actions and rewards on top. Practical versions limit the model class, as in our toy.
def solomonoff_next(observed: str, max_period=12):
"""Mix all 'repeat pattern p' programs, prior 2^-(len(code))."""
w = {"0": 0.0, "1": 0.0}
for per in range(1, max_period + 1):
for bits in product("01", repeat=per):
pat = "".join(bits)
gen = (pat * (len(observed) // per + 2))
if gen.startswith(observed):
prior = 2.0 ** -(2 * per + 1) # per ones, a zero, then per bits
w[gen[len(observed)]] += prior
z = w["0"] + w["1"]
return {b: round(v / z, 4) for b, v in w.items()}
for obs in ["0", "01", "0101", "011011", "0110110110"]:
print(f"{obs:<11} -> P(next) = {solomonoff_next(obs)}")
0 -> P(next) = {'0': 0.7501, '1': 0.2499}
01 -> P(next) = {'0': 0.7501, '1': 0.2499}
0101 -> P(next) = {'0': 0.9723, '1': 0.0277}
011011 -> P(next) = {'0': 0.9925, '1': 0.0075}
0110110110 -> P(next) = {'0': 0.0001, '1': 0.9999}
After "0", the period-1 pattern "repeat 0" (a 3-bit code) has the biggest weight. After "0110110110", only period 3 and its multiples still fit, plus a few long patterns. They all but agree on "1".
K ranks a random string as the most complex. But a random string is boring. A DNA genome or a proof feels "complex" in a different way. It is structured and hard to make.
Logical depth (Bennett 1988). The running time of near-shortest programs:
depths(x) = min { time(p) : U(p) = x, |p| ≤ K(x) + s }
Deep objects hold the result of a long computation. You cannot shortcut it.
Sophistication (Koppel 1987). Split x's shortest description into model plus noise. The model is a finite set S ∋ x. The noise is x's index in S, which costs log|S| bits. Sophistication is the smallest K(S) over sets with K(S) + log|S| ≤ K(x) + c. So the two-part code is still near-optimal.
Related: Gell-Mann & Lloyd's effective complexity, and the Kolmogorov structure function.
| Object | K | Depth | Sophistication |
|---|---|---|---|
| 0n | low | low | low |
| n coin flips | high | low (just print) | low (model: "any string") |
| digits of π | low | moderate | low |
| a genome | high | high | high |
| a long proof or chess table | low–mid | high | mid |
Bennett proved that depth cannot grow fast. A fast, simple process cannot create a deep object. Only long computations can. This gives a formal sense to "evolution took billions of years to make that".
Both measures depend on a significance parameter s or c. Both are even less computable than K.
def C(x: bytes) -> int:
"""Best of three real compressors, in bytes. An upper bound on K(x) + O(1)."""
return min(len(zlib.compress(x, 9)), len(lzma.compress(x, preset=9)), len(bz2.compress(x, 9)))
samples = {
"zeros": bytes(N),
"'ab' repeated": b"ab" * (N // 2),
"English-ish": (b"the quick brown fox jumps over the lazy dog " * 3000)[:N],
"pi digits": pi_digits(N).encode(),
"random bytes": rng.randbytes(N),
}
for name, x in samples.items():
print(f"{name:<15}{len(x):>8}{len(zlib.compress(x, 9)):>8}"
f"{len(lzma.compress(x, preset=9)):>8}{len(bz2.compress(x, 9)):>8}")
x = samples["pi digits"]
print(f"pi: {8 * C(x) / len(x):.3f} bits/digit vs log2(10) = {math.log2(10):.3f}")
input raw zlib lzma bz2 zeros 100000 120 148 47 'ab' repeated 100000 122 152 43 English-ish 100000 358 192 171 pi digits 100000 48174 43732 43229 random bytes 100000 100041 100064 100808 pi: 3.458 bits/digit vs log2(10) = 3.322
N = 100_000, rng = random.Random(42). pi_digits uses Machin's formula with Python integers. It is 10 lines, in the test file.
pi_digits is that short program.They find repeats and skewed statistics. They miss arithmetic, physics and logic. So C(x) can sit far above K(x). The gap never shows itself. Large language models are much stronger compressors, and "compression = intelligence" is an active research idea (the Hutter Prize).
It is only an upper bound, up to a constant. It can be wildly loose, as with π. You can never prove it is tight.
"K(0110) = 3" means nothing. The invariance constant is often thousands of bits. Use K for long objects and asymptotics.
Entropy belongs to a distribution. K belongs to one string. A fair coin (maximum entropy) can output 0n, which has tiny K.
They differ by up to 2 log n. Plain C breaks subadditivity and Levin–Schnorr. Say which one you mean.
Mixing compressors breaks the [0,1] range. Files bigger than the window (32 KB for zlib) look unrelated. Headers make tiny files look far apart.
Noise has the highest K. For "meaningful" structure, look at depth, sophistication, or the model part of MDL.
| Result | Statement |
|---|---|
| Invariance | |KU − KV| ≤ c |
| Counting | < 2n−c strings have C < n−c |
| Coding theorem | K(x) = −log m(x) + O(1) |
| Entropy | E[K] = H + O(K(P)) |
| Symmetry | I(x:y) = I(y:x) + O(1) |
| Chaitin | F can't prove K(x) > LF |
| Term | Meaning |
|---|---|
| C(x) | Plain complexity: shortest program for x, any programs allowed. |
| K(x) | Prefix complexity: shortest program, from a prefix-free set. |
| Universal machine U | One machine that can run every other machine's programs. |
| Invariance constant | Fixed cost of switching machines. It does not depend on x. |
| Incompressible | C(x) ≥ |x| (or ≥ |x| − c). No pattern to exploit. |
| Dovetailing | Run all programs in turns, a few steps each, so none blocks. |
| Upper semi-computable | You can compute better and better upper bounds, never knowing when you are done. |
| Prefix-free code | No word starts another word. Words end on their own. |
| Kraft inequality | ∑ 2−len ≤ 1 for any prefix-free code. |
| m(x) | Universal prior: chance a random program prints x. |
| Term | Meaning |
|---|---|
| K(x | y) | Shortest program for x when y is given for free. |
| I(x:y) | Shared information: K(x) + K(y) − K(x,y). |
| NCD | Normalized compression distance: a real-compressor stand-in for NID. |
| Martin-Löf test | A computable way to flag strings as "too regular". |
| Randomness deficiency | |x| − K(x|n): how many bits of pattern x has. |
| Ω (Chaitin) | Chance a random prefix program halts. Random and uncomputable. |
| MDL | Pick the model that minimizes model bits + data-given-model bits. |
| Solomonoff induction | Predict by weighting every program by 2−length. |
| Logical depth | Run time of near-shortest programs. "Hard to make." |
| Sophistication | Size of the smallest good model (set S) for x. |