之前的《疑难杂症,面试遇到根本不懂的题目怎么办》中留了一下问题,“如果想完全确定第10列缺失的数到底是什么,那么至少需要多少检验位?其实解决这个问题要从帽子问题来入手,帽子问题的基本规则就是玩家只能看到别人头上的帽子,而无法知道自己头上的帽子颜色。这其实是典型的”只错一位“情况下的纠错问题。
帽子问题的经典表述如下:个监狱关押着N个死囚。一天监狱长找到他们,并交给他们一个看起来很残酷的任务。规则是:一会给你们每人随机戴1顶帽子,而帽子要么是黑色要么是白色,概率相等,而且发的帽子当中必然有红有黑。你们看不到自己帽子的颜色,但是可以看到其他人的帽子颜色,你们之间不能有任何方式的交流。然后,你们每个人都可以猜测自己帽子的颜色,然后写在纸条上交给我,不能给其他人看到,当然,你们也可以放弃,那么就在纸条上写上“放弃”交给我。然后,如果你们当中有任何一个人猜错了帽子的颜色,你们将被全体处死;每个人猜测的结果可以公开,猜测的次数最多是参与人数的一半,如果你们猜对帽子颜色的人数不足一半,你们也将被全体处死,如果你们猜对帽色的人数达到一半或一半以上,同时又没有别人猜错,那么恭喜你们可以全体释放了!现在可以给你们分钟的时间考虑一个对你们有利的方案。这些死囚犯怎样才能抓住这难得的求生机会?请你为他们设想一个猜帽子颜色的方案。
那么这个猜帽子的问题要怎么解决呢,让我们先从N=4开始思考。假如我们把红帽子记为0,黑帽子记为1,那么站在第2个参与者的角度上看,他能得到的两组解分别是1011和0100。
一、如果他看到其它人都是0或者都是1那么他就可以猜到完全相反的答案。
二、如果第2个参考者看到的是1?10或者0?01的情况也就是说其它人头上红、白帽子都有,那么此时又要分为两种情况,
1.第一种情况是有其它玩家在第一轮猜测是就说出答案了,那么这时2号参与者头上的帽子颜色一定是与第一轮说出答案的参与者头上帽子颜色相反。
2.第二种情况是第一轮没有玩家猜出答案,这时2号玩家一定可以判断出自己头上的帽子颜色与帽子颜色占少数的玩家帽子颜色相同,比如2号玩家看到其它参与者头上两个红帽子,一个黑帽子,而带黑帽子的玩家在第一轮不敢给出答案,这时2号参与者头上一定是黑帽子,因为如果他带的是红帽子那么,那么有三顶红帽子的情况下,带黑帽子的参与者必然会在第一轮就得出答案。
N>4的情况也可以依此类推。说到这我们就可以知道了,这个帽子问题其实就是要解决在错误码个数已知的情况下,将错误码找到并更正的算法,这样做的最大好处就是避免编码重传。
我们知道之前很多如串口数据、网络传输包一旦校验失败,则整包重传,而这个帽子问题所带来的海明码则不需要这么做,他可以在添加校验位的情况下,自动找到错误码位置并更正,避免了整包重传的资源浪费情况发生。
而接下来我们就可以回答校验位个数的问题了,由于以16位数据为例,在已知只有一位数据错的情况下,校验位需要表示的情况共有2^4=16种,也就是需要4位表示,而如果是1024位数据,那么需要表示2^10=1024种情况,也就是10位校验位。那么拓展一下如果有两位错呢?那么这种情况下由于两位数据是任意的,从概率上讲是独立事件,校验位翻倍即可。
而最经典的校验算法当属海明码。
海明码一般使用偶校验,也就是当参与校验的校验位1的个数为奇数,则校验位为1;反之1的个数为偶数时,则校验位为0。
例子:数据位1111的 偶校验就是 11110
一般来说单纯的偶校验只能检测一位数据是否有错,但无法纠错。
如我们刚刚所说,我们的校验位所能表示的情况数量必须要大于数据流总长度,也就是2^校验位数 >= 校验位数 + 数据位数 +1
以数据位取4为例,代入可得校验位等于3
海明码的2的正整数次幂(如1,2,4,8,16,32)的位置都是校验位,其余都是数据位,如下图:
位置 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
用途 |
校验位 |
校验位 |
数据位 |
校验位 |
数据位 |
数据位 |
数据位 |
接下来需要确认1,2,4这些校验位要用来校验哪些数据位。
首先把所有位置的二进制码表示写出来,左补齐至校验位个数,如本例中校验位为3,那么左补0使二进制码长度满3位。
位置 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
用途 |
校验位 |
校验位 |
数据位 |
校验位 |
数据位 |
数据位 |
数据位 |
所在位置二进制码 |
001 |
010 |
011 |
100 |
101 |
110 |
111 |
其中校验位左边的0是*表示,也就是可以指代任意多个0,右边的0用?表示,即只能代表一个0。如下:
位置 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
用途 |
校验位 |
校验位 |
数据位 |
校验位 |
数据位 |
数据位 |
数据位 |
所在位置二进制码 |
001 |
010 |
011 |
100 |
101 |
110 |
111 |
校验位通配符表示 |
*1 |
*1? |
|
1?? |
|
|
|
接下来将所有数据位按照上述匹配规则进行分组。
位置 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
用途 |
校验位 |
校验位 |
数据位 |
校验位 |
数据位 |
数据位 |
数据位 |
所在位置二进制码 |
001 |
010 |
011 |
100 |
101 |
110 |
111 |
校验位通配符表示 |
*1 |
*1? |
匹配*1与*1?;即1、2两组 |
1?? |
匹配*1与1??;即1、4两组 |
匹配*1?与1??;即1、4两组 |
匹配*1、*1?与1??即匹配所有组 |
纵向的匹配分组如下:
校验位位置 |
1 |
2 |
4 |
校验位通配符表示 |
*1 |
*1? |
1?? |
匹配结果 |
001(1) |
010(2) |
100(4) |
011(3) |
011(3) |
101(5) |
|
101(5) |
110(6) |
110(6) |
|
111(7) |
111(7) |
111(7) |
因此我们可以确定
校验位1 负责校验1、3、5、7四位
校验位2 负责校验2、3、6、7四位
校验位4 负责校验4、5、6、7四位
假如要传递的数据为1110,那么如果进行偶校验,那么这段汉明码应该为1111110
我们刚刚也提到了1、2、4三个校验位将全部数据分为三组,那么不论哪一位出错,都可以得校验失败的结论,这个并不难理解。而海明码的纠错原理如下:
位置 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
用途 |
校验位 |
校验位 |
数据位 |
校验位 |
数据位 |
数据位 |
数据位 |
所在位置二进制码 |
001 |
010 |
011 |
100 |
101 |
110 |
111 |
校验位通配符表示 |
*1 |
*1? |
匹配*1与*1?;即1、2两组 |
1?? |
匹配*1与1??;即1、4两组 |
匹配*1?与1??;即1、4两组 |
匹配*1、*1?与1??即匹配所有组 |
所属分组 |
1 |
2 |
1、2 |
4 |
1、4 |
2、4 |
1、2、4 |
然后你会发现有以下几种情况:
而且海明码还有一个快速确定错误位置的算法,
1.分别对每个组校验,通过的记为0,出错的记为1.
2、将校验结果按照组别从大到小排列起来,得到一串1和0的组合。
假如我们刚刚接收的海明码序列为1111111,那么得到的校验结果从大到小排除就是111,这也就对应了出错位置为111二进制码所对应的位置即第7位,
如果接收的海明码序列为1111010,校验结果由大到小排除为101则可知出错位置为第5位。每每看到这都觉得海明这个人实在是个天才。