ELEC3203 · Digital Coding & TransmissionInformation Theory Fundamentals
Information Theory · Lecture notesProf. Celik
ELEC3203 · Digital Coding and Transmission
Lecture 2
Efficient Source Coding: Shannon-Fano and Huffman
BCD wastes bits whenever symbols are not equally likely. This lecture builds two concrete codes, Shannon-Fano and Huffman, that assign short codewords to common symbols and long ones to rare symbols, pushing the average length down to nearly the entropy bound.
Learning Objectives
Construct Shannon-Fano and Huffman codes for a given symbol alphabet
Compute average codeword length and coding efficiency against the entropy bound
Explain why Huffman coding is optimal among prefix codes
▸ Maximum entropy▸ Coding efficiency▸ Shannon-Fano coding▸ Huffman coding
Lecture 2
Contents
This page grows as further lectures are added to the document.
Notation used in this lecture
\(H\)
source entropy, in bits/symbol (from Lecture 1)
\(\ell_i\)
codeword length assigned to symbol \(m_i\), in bits
\(\bar L\)
average codeword length, \(\bar L = \sum_i p_i \ell_i\), in bits/symbol
CE, \(\eta\)
coding efficiency, \(\eta = H/\bar L\)
prefix code
a code in which no codeword is the start of another, so it can be decoded unambiguously
\(\lambda\)
Lagrange multiplier used to enforce \(\sum_i p_i = 1\) in the maximum-entropy derivation
2.1 Maximum entropy: q-ary and binary sources
Where does \(H \le \log_2 q\) come from?
Lecture 1 stated, and proved in its appendix, that the entropy of a \(q\)-ary source is bounded by \(H \le \log_2 q\), with equality only for equiprobable symbols. Here we derive that result directly, by maximising \(H\) subject to the one constraint every probability distribution must satisfy: \(\sum_i p_i = 1\).
2.1.1 Lagrangian derivation for a q-ary source
Background: what is a Lagrangian, and why set its derivative to zero?
Suppose you want to maximise (or minimise) some function, but the variables are not free: they must also satisfy a constraint. Here we want to maximise entropy \(H(p_1,\ldots,p_q)\), but the \(p_i\) cannot be chosen independently — they must always sum to 1. Without the constraint, "maximise \(H\)" would have a silly answer (push some \(p_i\) to absurd values), so we need a way to search only among the probability vectors that are actually valid.
The Lagrangian is a trick for turning a constrained problem into an unconstrained one. We build a new function \(\mathcal{L}\) by taking the objective (\(H\)) and adding a penalty term for violating the constraint, weighted by a new variable \(\lambda\) (the Lagrange multiplier):
Notice that whenever the constraint \(\sum_i p_i = 1\) actually holds, the penalty term is exactly zero, so \(\mathcal{L} = H\) at any valid point. This means: any point that maximises \(\mathcal{L}\) while also satisfying the constraint automatically maximises \(H\) too. We have replaced "maximise \(H\) subject to a constraint" with "find a stationary point of \(\mathcal{L}\)", which is a completely ordinary calculus problem with no constraint left to worry about.
Why does setting a derivative to zero find a maximum? This is the basic first-order condition from single-variable calculus, just applied one variable at a time: at the very top of a smooth hill, the slope is momentarily flat — the function is neither increasing nor decreasing in any direction. So at a maximum, the partial derivative with respect to every variable (each \(p_i\), and also \(\lambda\)) must independently equal zero. That gives us one equation per variable, and solving all of them together pins down the maximising point exactly. (This condition alone finds any flat point — a peak, a valley, or a saddle; here we know from the shape of entropy, concave and zero at the boundaries, that the one flat interior point we find must be the maximum.)
This machinery, "Lagrange multipliers", is the standard tool in optimisation for handling equality constraints, and it reappears constantly in engineering (power allocation, resource budgets, rate-distortion problems). The mechanical recipe is always the same three steps: (1) form \(\mathcal{L}\) = objective + \(\lambda\)×(constraint written as "\(= 0\)"), (2) set every partial derivative of \(\mathcal{L}\) to zero, (3) solve the resulting system.
We want to maximise \(H = -\sum_{i=1}^q p_i\log_2 p_i\) subject to \(\sum_{i=1}^q p_i = 1\). Introduce a Lagrange multiplier \(\lambda\) and form the Lagrangian:
The right-hand side does not depend on \(i\) at all, so every \(p_i\) satisfies the same equation, meaning every \(p_i\) must be equal to the same constant \(c\). Applying the constraint \(\sum_i p_i = 1\) with \(p_i = c\) for all \(i\):
$$ \sum_{i=1}^{q} c = qc = 1 \quad\Longrightarrow\quad c = \frac{1}{q}. $$
(2.4)
So the entropy of a \(q\)-ary source is maximised exactly when every symbol is equally likely, \(p_i = 1/q\), which (as we already know) gives \(H_{\max} = \log_2 q\). This is the same conclusion as Lecture 1's Problem 2.3, reached this time via calculus rather than an inequality.
2.1.2 The binary case, revisited
Specialising to \(q=2\) with \(p_1 = p\) and \(p_2 = 1-p\) recovers the binary entropy function from Lecture 1, \(H(p) = -p\log_2 p - (1-p)\log_2(1-p)\), maximised at \(p=0.5\) with \(H_{\max}=1\) bit. The figure below is the same curve as Lecture 1, included again here because the whole of this lecture is about closing the gap between \(H\) and \(\log_2 q\).
Figure 2.1. Binary entropy H(p): a recap from Lecture 1. Efficient coding aims to make \(\bar L\) hug this curve, not the flat line \(\bar L = 1\) that BCD always uses. Drag the slider to read \(H(p)\) at any \(p\).
2.2 Efficient source coding
Coding efficiency and the design rule
We restrict attention to lossless source coding: converting symbols into codewords without throwing away any information, so the original symbols can always be recovered exactly. From Lecture 1, the information rate is
$$ R = R_s H \le R_s \log_2 q, $$
(2.5)
so in general BCD, which always spends \(\log_2 q\) bits per symbol, is not efficient. Coding efficiency compares the information the source truly produces with the rate the code actually uses:
Shannon's source coding theorem guarantees that efficient coding can push this arbitrarily close to 100%: for any memoryless source, there exists a code whose average length \(\bar L\) is as close to \(H\) as we like (exactly \(H\) when all self-informations happen to be integers, and within one bit of \(H\) in general, closer still if we code blocks of symbols together). This lecture builds two such codes, Shannon-Fano and Huffman.
2.2.1 The core idea: match codeword length to information content
When symbols are equiprobable, \(p_i = 1/q\), entropy is already maximal, \(H = \log_2 q\), and coding every symbol with \(\log_2 q\) bits (BCD) achieves \(\text{CE} = 100\%\) automatically. The trouble is only when symbols are not equiprobable: then \(H < \log_2 q\) and BCD is wasteful. The fix follows directly from Lecture 1's definition of information content, \(I(m_i) = -\log_2 p_i\):
Design rule. Assign each symbol a codeword whose length \(\ell_i\) is as close as possible to its own information content \(I(m_i) = -\log_2 p_i\): common symbols get short codewords, rare symbols get long ones. Two practical constraints bind this ideal: codeword lengths must be whole numbers of bits, and no codeword may be a prefix of another, so a decoder can tell where one codeword ends and the next begins just by reading bits left to right.
EXAMPLE 2.1 · Same alphabet, two probability profiles
Eight symbols \(m_1,\ldots,m_8\) are BCD-coded with the fixed 3-bit codewords 000 through 111. Compare the coding efficiency when the symbols are equiprobable against when they follow \(p = (0.27, 0.20, 0.17, 0.16, 0.06, 0.06, 0.04, 0.04)\).
1
BCD codeword length
BCD always uses \(\bar L = 3\) bits/symbol here, since \(\log_2 8 = 3\).
2
Equiprobable case
\(p_i = 1/8\) for all \(i\), so \(H = \log_2 8 = 3\) bits/symbol and
Same alphabet, same fixed-length code, but efficiency drops from 100% to 89.69% purely because the symbols are no longer equally likely. This 10.31 percentage-point gap is what Shannon-Fano and Huffman coding recover.
2.3 Shannon-Fano coding
Split the sorted list from the top down
Shannon-Fano coding builds a variable-length prefix code by repeatedly splitting the symbol list into two halves of roughly equal total probability.
1
Order symbols by descending probability
3
Assign bit 0 to the top half, bit 1 to the bottom half
2
Split the group into two halves of nearly equal total probability
4
Recurse on each half, until only 1 symbol remains
Read off each symbol's codeword top to bottom — the result is automatically a prefix code
Figure 2.2.
2.3.1 Worked example: eight symbols
Take the same eight symbols as Example 2.1, with \(p = (0.27, 0.20, 0.17, 0.16, 0.06, 0.06, 0.04, 0.04)\), already sorted in descending order. Splitting repeatedly to keep each half's probability mass as equal as possible gives:
Symbol
\(p_i\)
\(I_i \approx\) (bits)
Step 1
Step 2
Step 3
Step 4
Codeword
m₁
0.27
1.89
0
0
00
m₂
0.20
2.32
0
1
01
m₃
0.17
2.56
1
0
0
100
m₄
0.16
2.64
1
0
1
101
m₅
0.06
4.06
1
1
0
0
1100
m₆
0.06
4.06
1
1
0
1
1101
m₇
0.04
4.64
1
1
1
0
1110
m₈
0.04
4.64
1
1
1
1
1111
Figure 2.3. Each Coding Steps column is boxed independently: cells sharing a coloured frame are still one subgroup at that step; a cell boxed alone has already split down to a single symbol. Step 1: {m₁,m₂} vs {m₃–m₈}. Step 2: {m₁}, {m₂}, {m₃,m₄}, {m₅–m₈}. Step 3: {m₃}, {m₄}, {m₅,m₆}, {m₇,m₈}. Step 4: {m₅}, {m₆}, {m₇}, {m₈}.
Notice the pattern: the two most probable symbols get 2-bit codewords, the middle two get 3 bits, and the four rarest share 4 bits each, tracking their information content \(I_i\) closely.
Grouping by codeword length, \(0.27+0.20=0.47\) of the mass uses 2 bits, \(0.17+0.16=0.33\) uses 3 bits, and \(0.06+0.06+0.04+0.04=0.20\) uses 4 bits. The average codeword length is:
Compare this with the 89.69% of fixed 3-bit codewords on the very same symbols: variable-length coding recovers almost all of the wasted 10.31 percentage points, using only whole numbers of bits per symbol.
2.3.2 A cleaner example: powers-of-two probabilities
Shannon-Fano reaches exactly 100% efficiency whenever every probability is a negative power of two, because then each split can be made perfectly even. Take eight symbols with \(p = (\tfrac12, \tfrac14, \tfrac18, \tfrac1{16}, \tfrac1{32}, \tfrac1{64}, \tfrac1{128}, \tfrac1{128})\):
Symbol \(X_i\)
Prob. \(P(X_i)\)
\(I\) (bits)
Coding steps
Codeword
bits/symbol
1
2
3
4
5
6
7
A
\(\frac{1}{2}\)
1
0
0
1
B
\(\frac{1}{4}\)
2
1
0
10
2
C
\(\frac{1}{8}\)
3
1
1
0
110
3
D
\(\frac{1}{16}\)
4
1
1
1
0
1110
4
E
\(\frac{1}{32}\)
5
1
1
1
1
0
11110
5
F
\(\frac{1}{64}\)
6
1
1
1
1
1
0
111110
6
G
\(\frac{1}{128}\)
7
1
1
1
1
1
1
0
1111110
7
H
\(\frac{1}{128}\)
7
1
1
1
1
1
1
1
1111111
7
Figure 2.4. At each bit position the still-active group splits: the coloured cell that turns 0 is the symbol exiting with that codeword; every other still-active symbol carries a 1 into the next column. Because each split is exactly even (probabilities are powers of two), this splitting is a straight comb, and no bit is ever wasted.
Here every codeword length exactly equals its symbol's information content, so \(H = \bar L\) precisely:
$$ H = \bar L = \tfrac12(1)+\tfrac14(2)+\tfrac18(3)+\tfrac1{16}(4)+\tfrac1{32}(5)+\tfrac1{64}(6)+2\cdot\tfrac1{128}(7) = \frac{127}{64} \approx 1.984 \ \text{bits/symbol}, $$
(2.11)
giving \(\text{CE} = 100\%\). By contrast, a fixed 3-bit code for these 8 symbols (needed because \(\lceil\log_2 8\rceil = 3\)) would give only \(\text{CE} = 1.984/3 = 66\%\). This is the cleanest possible illustration of the design rule from Section 2.1: match codeword length to information content, exactly, whenever the probabilities allow it.
2.4 Huffman coding
Merge the least probable items from the bottom up
Huffman coding builds the same kind of variable-length prefix code as Shannon-Fano, but from the bottom up instead of the top down: it repeatedly merges the two least probable symbols into a combined subgroup, rather than repeatedly splitting the whole list in two.
1
Arrange symbols/subgroups by descending probability
3
Label the merge: 0 to the higher-probability branch, 1 to the lower
2
Merge the two least probable items into one new subgroup
4
Repeat on the shortened list until one item remains
Read each codeword bottom-up from the tree's root, then reverse the bit order
Figure 2.5.
2.4.1 Worked example: the same eight symbols
Apply Huffman coding to the same alphabet as Section 2.3.1, \(p = (0.27, 0.20, 0.17, 0.16, 0.06, 0.06, 0.04, 0.04)\), so the two methods can be compared directly. Building the tree takes 7 merges (one per internal node); each step below shows the currently active items sorted by probability and the two least probable highlighted for merging, and the new combined item that carries into the next step.
higher probability → bit 0lower probability → bit 1
↓ tie at 0.20: merge m₂(0)+m₅₆₇₈(1) → m₂,₅₆₇₈ = 0.40
step 5m₂,₅₆₇₈ .40 m₃₄ .33m₁ .27
↓ merge m₃₄(0)+m₁(1) → m₁,₃₄ = 0.60
step 6m₁,₃₄ .60m₂,₅₆₇₈ .40
↓ final merge m₁,₃₄(0)+m₂,₅₆₇₈(1) → root = 1.00
step 7root = 1.00 (tree complete)
Codeword so far · each merge prepends its bit
Symbol
\(p_i\)
Merge 1
Merge 2
Merge 3
Merge 4
Merge 5
Merge 6
Merge 7
m₁
0.27
1
01
m₂
0.20
0
0
10
m₃
0.17
0
0
00
000
m₄
0.16
1
1
01
001
m₅
0.06
0
00
00
100
100
1100
m₆
0.06
1
01
01
101
101
1101
m₇
0.04
0
0
10
10
110
110
1110
m₈
0.04
1
1
11
11
111
111
1111
Figure 2.6. Huffman merges for the eight-symbol example. At each step the two least probable items (highlighted) merge, and the combined item carries into the next step. The table tracks each symbol's codeword: a merge prepends 0 (blue) to every symbol in the higher-probability item and 1 (amber) to every symbol in the lower one, so codewords grow from the last bit back to the first.
The same construction is often easier to read as an actual tree: the root splits repeatedly until every leaf is a single symbol, and a symbol's codeword is just the string of 0s and 1s collected root-to-leaf.
bit 0 (higher-probability branch)bit 1 (lower-probability branch)
Symbol
\(p_i\)
Codeword
bits
m₁
0.27
01
2
m₂
0.20
10
2
m₃
0.17
000
3
m₄
0.16
001
3
m₅
0.06
1100
4
m₆
0.06
1101
4
m₇
0.04
1110
4
m₈
0.04
1111
4
Figure 2.7. The Huffman tree for this example. Grey boxes are internal (merged) nodes labelled with their combined probability; leaves show the symbol, its probability, and its final codeword. The table collects the codewords: each merge prepends 0 to every symbol under its higher-probability branch and 1 to every symbol under the lower one. The bit-reversal step matters: the tree is built bottom-up, so a naive top-down reading of each path would place bit assignments in the wrong order and could break the prefix property.
Reading a symbol's codeword means walking from the root down to its leaf and recording each 0/1 along the way, then reading that path bottom-up (root-first) is exactly the order the bits are transmitted in — for example m₅ sits inside m₅₆₇₈(bit 1 at the root) → m₅₆₇₈(bit 1) → m₅₆(bit 0) → m₅(bit 0), giving codeword 1100, exactly matching the table in Figure 2.7.
The length distribution is identical to Shannon-Fano's for this example (two 2-bit, two 3-bit, four 4-bit codewords), so:
The two methods reach the same efficiency here, though in general their individual codeword assignments (which symbol gets which specific bit string) can differ, even when the length distribution matches.
2.5 Comparing the two methods
Greedy split versus provable optimum
Shannon-Fano
Top-down: repeatedly split the sorted list to balance probability mass. Simple to reason about by hand, but the greedy split is not always globally optimal.
Huffman
Bottom-up: repeatedly merge the two least probable items. Provably produces the shortest possible average length among all prefix codes for the given probabilities.
Both belong to the class of entropy coding methods: they assign codeword length as close as possible to each symbol's information content.
Both produce prefix codes, so the encoded bit stream can always be decoded unambiguously.
For an alphabet of \(q\) symbols, the longest codeword in either scheme can be as long as \(q-1\) bits, which becomes impractical (large decoding buffers, high latency) for large alphabets.
Huffman coding is provably optimal (minimum \(\bar L\) among prefix codes); Shannon-Fano is usually close but not guaranteed optimal. In practice the coding efficiency of the two is nearly identical, as both worked examples above show.
MATLAB corner
MATLAB's Communications Toolbox ships huffmandict and huffmanenco/huffmandeco, but writing the design rule out by hand builds real intuition. The script below builds a Huffman code from scratch and checks it against Example 2.1's numbers.
huffman_from_scratch.m
MATLAB
% Build a Huffman code by hand and check it against Example 2.1.
p = [0.27 0.20 0.17 0.16 0.06 0.06 0.04 0.04];
symbols = 1:numel(p);
% MATLAB's Communications Toolbox builds the dictionary directly:
Apply Huffman coding to the second example of Section 3.2, the eight symbols A through H with probabilities \(\tfrac12,\tfrac14,\tfrac18,\tfrac1{16},\tfrac1{32},\tfrac1{64},\tfrac1{128},\tfrac1{128}\). Compare the resulting average length and efficiency with the Shannon-Fano result found in the notes.
Solution. Merging the two least probable symbols repeatedly, G(1/128) and H(1/128) merge first into 1/64. That merges with F(1/64) into 1/32, which merges with E(1/32) into 1/16, then with D(1/16) into 1/8, then with C(1/8) into 1/4, then with B(1/4) into 1/2, and finally with A(1/2) into the root. This chain of merges is exactly the structure of a "comb" tree, and it reproduces the same codeword lengths as Shannon-Fano: A gets 1 bit, B gets 2, C gets 3, and so on up to G and H sharing 7 bits each.
Since the lengths match Shannon-Fano exactly, \(\bar L_{\text{Huffman}} = 127/64 \approx 1.984\) bits/symbol, equal to \(H\), giving \(\text{CE}=100\%\), identical to the Shannon-Fano result. This is expected: when probabilities are exact negative powers of two, both methods reach the information-theoretic optimum, and there is only one sensible way to assign lengths.
PROBLEM 2.2
A source has five symbols with probabilities \(0.4, 0.2, 0.2, 0.1, 0.1\). (a) Find the entropy \(H\). (b) Design a Shannon-Fano code and find its average length and efficiency. (c) Would a fixed-length BCD code do better or worse, and by how much?
(b) Shannon-Fano code. Sorted: \(0.4, 0.2, 0.2, 0.1, 0.1\). First split as evenly as possible: \(\{0.4\}\) versus \(\{0.2,0.2,0.1,0.1\}\) (0.4 vs 0.6) is closer than any other cut, so symbol A (0.4) gets a lone 1-bit prefix "0". The remaining group \(\{0.2,0.2,0.1,0.1\}\) splits into \(\{0.2\}\) and \(\{0.2,0.1,0.1\}\) (0.2 vs 0.4): B gets "10". Then \(\{0.2,0.1,0.1\}\) splits into \(\{0.2\}\) and \(\{0.1,0.1\}\): C gets "110". Finally \(\{0.1,0.1\}\) splits into single symbols: D gets "1110" and E gets "1111". Codeword lengths: A=1, B=2, C=3, D=4, E=4.
(c) BCD comparison. With \(q=5\) symbols, BCD needs \(\lceil\log_2 5\rceil = 3\) bits/symbol fixed, giving \(\text{CE}_{\text{BCD}} = 2.122/3 = 70.7\%\). The Shannon-Fano code is substantially better, 96.5% versus 70.7%, because it lets the frequent symbol A use just 1 bit instead of being forced into 3 like every other symbol.
PROBLEM 2.3
For the same five symbols as Problem 2.2, \(p = 0.4, 0.2, 0.2, 0.1, 0.1\), build a Huffman code and compare its average length with the Shannon-Fano result from Problem 2.2.
Solution. Sort: \(A(0.4), B(0.2), C(0.2), D(0.1), E(0.1)\). Merge the two least probable, D and E, into \(DE = 0.2\). Now the list is \(A(0.4), B(0.2), C(0.2), DE(0.2)\): merge any two of the three tied 0.2's, say C and DE, into \(C\!D\!E = 0.4\). Now \(A(0.4), B(0.2), CDE(0.4)\): merge the two least probable, B and... here B(0.2) is smaller than both 0.4's, so merge B with either 0.4 group; merging B and A gives \(AB = 0.6\). Finally merge \(AB(0.6)\) and \(CDE(0.4)\) into the root.
Reading codeword lengths from this tree: A and B each took one merge step from the root within \(AB\) (2 bits each after the final merge), C, D, E each sit inside \(CDE\) which itself took one merge from the root, and D, E took one extra merge inside \(DE\). Working through the depths gives lengths A=2, B=2, C=2, D=3, E=3.
The average length is 2.2 bits/symbol, identical to Shannon-Fano's result in Problem 2.2, so \(\text{CE}=96.5\%\) again. This illustrates the point from Section 5: on many real examples the two methods land on the same efficiency even though the specific codeword-to-symbol assignment differs, because there was a tie to break among the 0.2 probabilities and either choice gives the same length distribution.
Summary and key formulas
Maximum entropy
$$H_{\max} = \log_2 q \text{ at } p_i = 1/q$$
Coding efficiency
$$\text{CE} = \eta = H/\bar L$$
Average codeword length
$$\bar L = \sum_i p_i \ell_i$$
Design rule
$$\ell_i \approx I(m_i) = -\log_2 p_i$$
Maximum entropy occurs at equiprobable symbols, \(H_{\max} = \log_2 q\), proved here by Lagrange multipliers.
Efficient coding assigns codeword length close to each symbol's information content; BCD only does this when symbols are already equiprobable.
Shannon-Fano splits the sorted symbol list top-down to balance probability mass at each step.
Huffman merges the least-probable symbols bottom-up, and is provably optimal among prefix codes.
Both are prefix codes, decodable unambiguously, and both push coding efficiency close to 100% wherever the underlying probabilities allow it.
Next, in Lecture 3: sources with memory, where correlation between symbols opens up further gains beyond what a memoryless entropy code can achieve.