Algorithm Search⌘K

Proofs: Kernighan's bit count

These are the human proofs of Kernighan's bit count, at full length.

Kernighan's loop returns the number of 1-bits and runs once per 1-bit full

This proof shows: correctness, termination.

Fix w≥1w \ge 1. A word is an integer xx with 0≤x<2w0 \le x < 2^w, with bits xix_i such that x=∑i=0w−1xi2ix = \sum_{i=0}^{w-1} x_i 2^i. Let ν(x)=∑ixi\nu(x) = \sum_i x_i.

Lemma. For each word x≠0x \ne 0, the value x∧(x−1)x \wedge (x - 1) is a word, and ν(x∧(x−1))=ν(x)−1\nu(x \wedge (x - 1)) = \nu(x) - 1. More exactly, it is xx with its lowest 1-bit cleared.

Proof. Since x≠0x \ne 0, xx has a lowest 1-bit; call its position jj. So xj=1x_j = 1 and xi=0x_i = 0 for i<ji < j. Then x=h+2jx = h + 2^j with h=∑i>jxi2ih = \sum_{i > j} x_i 2^i, and hh is a multiple of 2j+12^{j+1}. Since x≥1x \ge 1, x−1=h+2j−1=h+∑i<j2ix - 1 = h + 2^j - 1 = h + \sum_{i < j} 2^i is a word with no wrap-around. Its bits are: xix_i for i>ji > j (from hh), 0 at position jj, and 1 at each position i<ji < j. The AND of xx and x−1x - 1 has bit xi∧xi=xix_i \wedge x_i = x_i at each i>ji > j, bit 1∧0=01 \wedge 0 = 0 at jj, and bit 0∧1=00 \wedge 1 = 0 at each i<ji < j. So x∧(x−1)=hx \wedge (x - 1) = h, which is xx with bit jj cleared, and ν(h)=ν(x)−1\nu(h) = \nu(x) - 1. □\square

Theorem. For each word x0x_0, KERNIGHAN-POPCOUNT(x0)(x_0) stops after exactly ν(x0)\nu(x_0) iterations of the loop and returns ν(x0)\nu(x_0).

Proof. We use this invariant of the loop of line L2.

(I) xx is a word, c≥0c \ge 0, and c+ν(x)=ν(x0)c + \nu(x) = \nu(x_0). Also, cc equals the number of iterations done so far.

Initialization. Before the first test, line L1 gives c=0c = 0 and x=x0x = x_0, so (I) holds.

Maintenance. Assume (I) and that the test of line L2 is true, so x≠0x \ne 0. By the lemma, line L3 makes xx a word with ν\nu one smaller. Line L4 makes cc one larger, and one more iteration is done. So c+ν(x)c + \nu(x) does not change, and (I) holds again.

Termination. By (I), ν(x)=ν(x0)−c\nu(x) = \nu(x_0) - c, and each iteration raises cc by exactly 1. Since ν(x)≥0\nu(x) \ge 0, at most ν(x0)\nu(x_0) iterations can run. While ν(x)>0\nu(x) > 0 we have x≠0x \ne 0 and the loop goes on, and ν(x)=0\nu(x) = 0 holds exactly when x=0x = 0. So the loop stops exactly when c=ν(x0)c = \nu(x_0), after ν(x0)≤w\nu(x_0) \le w iterations. Line L5 returns c=ν(x0)c = \nu(x_0). ■\blacksquare

Remark on signed words. The lemma needs x−1x - 1 to be computed without overflow. For an unsigned word x≠0x \ne 0 this holds. In a signed ww-bit type, x=−2w−1x = -2^{w-1} gives x−1<−2w−1x - 1 < -2^{w-1}, which is outside the range; in C this is undefined behaviour. In arithmetic modulo 2w2^w the lemma applies to the bit pattern, which is why the algorithm is stated for unsigned words.

Back to Kernighan's bit count