Algorithm Search⌘K

Proofs: Soundex

These are the human proofs of Soundex, at full length.

SOUNDEX computes the code that the four Soundex rules define full

This proof shows: correctness.

Let s=s[0…L−1]s = s[0 \ldots L-1] be the name after line L1: upper-case ASCII letters only, with L≥1L \ge 1. Indices are 0-based. Let code\mathrm{code} be the digit map of the page, with code(a)=0\mathrm{code}(a) = 0 for A, E, I, O, U, Y, H and W.

The rules. For 1≤i<L1 \le i < L, let p(i)p(i) be the largest index j<ij < i with s[j]∉{H,W}s[j] \notin \{\mathrm{H}, \mathrm{W}\}, or p(i)=0p(i) = 0 if there is no such j≥1j \ge 1. Position i≥1i \ge 1 is kept when code(s[i])≠0\mathrm{code}(s[i]) \ne 0 and code(s[i])≠code(s[p(i)])\mathrm{code}(s[i]) \ne \mathrm{code}(s[p(i)]). The digit string Δ\Delta is the sequence of code(s[i])\mathrm{code}(s[i]) over the kept positions ii, in increasing order of ii. The Soundex code is s[0]s[0] followed by the first three digits of Δ\Delta, with zeros added at the end when Δ\Delta has fewer than three digits. This is the definition of the page: the first letter stays (rule 1), letters get digits (rule 2), a letter with the same digit as the last letter before it, with only H and W between them, is dropped (rule 3), and vowels, H and W after the first letter are dropped and the result has length 4 (rule 4).

Invariant. At the test of line L3 for the letter s[i]s[i], with 1≤i≤L1 \le i \le L (i=Li = L means the loop ends):

  1. last=code(s[q])last = \mathrm{code}(s[q]), where qq is the largest index j<ij < i with j=0j = 0 or s[j]∉{H,W}s[j] \notin \{\mathrm{H}, \mathrm{W}\};
  2. outout is s[0]s[0] followed by the codes of the kept positions in 1…i−11 \ldots i-1, and it has at most 3 digits.

Initialization. Line L2 sets out=s[0]out = s[0] and last=code(s[0])last = \mathrm{code}(s[0]). For i=1i = 1, q=0q = 0, and no position in 1…01 \ldots 0 exists. Both parts hold.

Maintenance. Take the step for s[i]s[i], with i<Li < L. For i≥1i \ge 1, the index qq of part 1 equals p(i)p(i) by the two definitions. If s[i]s[i] is H or W, line L5 skips the letter. H and W have code 0, so position ii is not kept, and qq for i+1i + 1 is the same as for ii, because s[i]s[i] is H or W. Both parts hold. Otherwise line L4 sets c=code(s[i])c = \mathrm{code}(s[i]), and by part 1, last=code(s[p(i)])last = \mathrm{code}(s[p(i)]). So the test of line L6 is true exactly when position ii is kept, and then line L6 appends its digit. Line L7 sets last=code(s[i])last = \mathrm{code}(s[i]), and qq for i+1i + 1 is ii, because s[i]s[i] is not H or W. Both parts hold.

Early stop. Line L8 stops the loop when outout has 3 digits. These are the first three digits of Δ\Delta by part 2, and the code uses no later digit, so the rest of the scan does not change the result.

End. If the loop ends without line L8, part 2 for i=Li = L says that outout is s[0]s[0] followed by all of Δ\Delta, with at most 3 digits. Line L9 adds zeros to length 4. In both cases the result is s[0]s[0] followed by the first three digits of Δ\Delta, padded with zeros. This is the Soundex code. ■\blacksquare

The loop runs at most LL times with O(1)O(1) work each, so the time is O(L)O(L), and line L1 takes time linear in the length of the input.

Back to Soundex