汉明码 (Hamming Code) 亦称海明码。在计算机科学和电信领域,数据在不同信道中传输时不可避免会产生错误,解决的办法通常是在原始数据中插入额外的校验数据,有两种策略:1) 增加尽量少的冗余数据,使接收方可以判断出数据出错 (检错码) ;2) 增加足够多的冗余数据,使接收方可以推断出正确数据 (纠错码) 。汉明码就是一种线性纠错码。
我们先来说明一些概念:
假设一份原始数据的长度是 m 比特 (bit) ,对应的校验码是 r 比特,那么两者合在一起长度便是 n = m + r
,整个数据块被称作代码字 (codeword) 。它的码率 (code rate) 便是非冗余数据的占比,即 m/n 。
那什么是错误呢?假设发送方发出一个代码字 10001010,接收方收到了 10011001,很容易看到,有 3 个比特位发生了变化 (两个代码字进行异或运算后其结果中 1 的个数),这就是错误。我们把差异的比特位个数称作汉明距离 (Hamming distance) 。显而易见,如果两个代码字之间的汉明距离是 d,那么就需要发生 d 个比特位错误才能从一个变成另一个。
现在让我们来想象这样一个场景:
我有一套具体的、确定的编码表,因此我可以找出表中两两编码之间最小的一个汉明距离,这个值就是这套编码表的汉明距离,假设记成 k 。那我就可以很有信心的说,如果在传输中发生小于 k 个比特位的错误,我都可以发现,因为这样的错误无法把一个表中的代码字变成另外一个,所以当我接收到一个不在表中的编码,我就知道有错误发生了。那我最多能纠正多少个位数的错误呢?答案是 (k - 1) / 2 个,可以这样思考:假设一个编码 A 发生错误变成了 B,如果 AB 之间的汉明距离比 B 与其他正确编码之间的距离都小,那我就可以确信,B 对应的正确编码是 A 而不是其他。所以要纠正 n 个错误,我就需要编码表的汉明距离至少是 2n + 1 。
举个具体的例子,这是一套只有四个码的编码表:
000111, 111000, 000000, 111111
它的汉明距离是 3 。所以只要发生的错误小于 3 都可以被发现,比如 010000, 100111, 000011 。另外,如果只发生 1 位错误 (),我们就可以纠正成正确的编码,比如 010000 ,它距离 000000 最近,所以原始数据一定是 000000 。
现在如果我们要设计这样一套编码,它用 m 位字节传输信息,r 位用于校验,从而实现 1 位的纠错能力。可见,距离 1 表示 1 位出错,那么它对应的错误码就是其中某一位翻转了,这种可能性便有 n (n = m + r) 种。因此对于 个合法编码中的任意一个,都会对应 n 个距离是 1 的非法代码字,它一共需要分配 n + 1 个编码。因为总共可分配的编码数量是 ,所以我们可以得到这样的条件公式:
当给定一个 m 值时,就可以计算出满足这种条件下的最小校验码位数。
那如何具体去设计校验码的值呢?理查德·卫斯里·汉明 (Richard Wesley Hamming) 发明了一种方法,这就是汉明码,其内容如下:
首先它对代码字中的每一个比特位标上序号,从左向右,从 1 开始。其中序号是 2 的幂次方的比特位被分配成校验位 (比如 1, 2, 4, 8, 16…) ,其余序号的比特位则用来填充信息数据 (比如 3, 5, 6, 7, 9…) 。
每一个校验位都会被指定负责几个特定序号的数据位,它需要保证的是:它本身的数值和所有它负责的数据位之和是偶数。
那如何知道一个校验位需要负责的数据位序号呢?这里是这样定义的:给定一个数据位序号 x,把它拆分成 2 的幂次方数之和,各个幂次方数对应的序号,即它所要参与计算的校验位。比如 x = 5,5 = 1 + 4,所以 1 和 4 这两个校验位都要把 5 号数据位纳入自己负责的范围 (是否已经感受到了其巧妙之处) 。
我们结合下图的例子来看:
图中一个代码字包含 7 位数据和 4 位校验,当接收到数据后,接收方就会依次进行 4 次校验计算,其校验结果序列 (低序号校验结果排在低位,即右侧) 被称为错误伴随式 (error syndrome) ,如果值为 0 则表示没有错误,否则就表示有错误发生,其二进制对应的数值就是错误比特位的序号 (是不是很神奇) ,如图,传输过程产生了 1 比特错误,其校验结果伴随式是 0101,其值为 5,所以只要把第 5 位的比特位翻转就可以纠正数据了。
最后我们来探究一下为何如此神奇。前面我们提到,一个序号为 x 的数据位,对它负责的校验位序号,其实就是 x 的二进制值中 1 所出现的位置所代表的数值。
比如,我们以 5 为例,它的二进制值是 0101,从低位往高位看 (从右往左) ,它是 , 这两个数的和,负责校验它的校验位序号就会是 1 () 和 4 () 。
想象一下,如果这个数据位发生错误,那么对它负责的校验位计算结果势必会变成奇数 (1) ,而其他校验位将不受影响,还是偶数 (0) 。所以当这些校验结果仍然按从右往左的顺序还原成二进制值时,它自然就指示出了错误比特位的序号。这就是其设计的精妙所在。