关于海明校验码的思考和分享

1,009 阅读9分钟

前言

        最近这段时间在学习计算机组成原理,初见海明校验码,其实现的效果让我十分惊喜,但具体的原理却有些模糊不清,经过一段时间的资料查找和思考,逐渐有了眉目,也产生了一些有趣的思考。这篇博客和大家介绍一下海明校验码以及自己的一些思考。

一、异或运算

        在计算机中,数据以及指令都是以二进制的形式储存的,也就是以1和0的形式,在逻辑运算中存在异或运算这一种运算方式。异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1)

二、奇偶校验码

2.1 为什么需要校验码

        首先我们聊一聊为什么需要校验码,在数据的传输或者储存过程中,比如说两台计算机之间的通信或者在一台计算机中,将数据储存在磁盘的时候,数据都可能发生跳变。

        拿通信来说,接收方只会接受到0和1的序列号,并不知道这是否是真实的数据,因此,我们尝试想一种方法,通过增加一个冗余位 来达到校验的目的。

2.2 奇偶校验码

        比如我们有这样一串二进制数字1010,也就是十进制的10,我们计算这4位数字的异或运算结果,即1⊕0⊕1⊕0=0。根据每一串二进制数各个位置异或运算的结果,我们发明了奇偶校验码。

        所谓奇校验方式,就是通过在原数据位前面或后面增加一个冗余位,来使得整个校验码异或运算的结果为1,比如1010这一串二进制数,我们可以在前面增加一个1,来使得整个字串最终异或运算的结果为1。

        同样的道理,所谓偶校验方式,就是通过在原数据位前面或后面增加一个冗余位,来使得整个校验码异或运算的结果为0,也就是在1010前面增加冗余位0。这样,便构成了奇校验码/偶校验码。

        整个校验码中,表示数据的1010称作数据位,而增加的1/0便称为校验位

        因此,如果采用偶校验的方式,只要数据的接收方对校验码进行异或运算,结果为1,说明数据失真,可要求对方重新传输。

2.3 奇偶校验码的不足

        聊到现在,大家也很容易看出来奇偶校验码存在的不足。首先,我们只能知道数据是否出现错误,但不能了解到是哪一位出现了错误,无法进行自动的纠正。同时,由于异或运算的特性,当数据有偶数个出现问题的时候,异或运算的结果依旧是正确的。如下图:

uTools_1645425020677.png

三、海明校验码的构建

        为了弥补奇偶校验码的不足以及在一定程度上实现纠错功能。Richard Hamming于1950年提出了海明校验码。这也是他1968年入选图灵奖的重要贡献之一。

3.1 十进制转二进制

        在正式聊海明码如何构建之前,我们回顾一个老生常谈的问题。把一个十进制数字转换为二进制数字,怎么转。最常见的一个通法是除二取余,逆向排序,如下图所示:

uTools_1644502108066.png         我们通过不断除以2,记录下余数,最后由下向上排列的方式成功地实现了十进制转二进制,当然,这个除r取余,逆向排序是适用于十进制转任何r进制的。但如果这个十进制数字比较小,我们还可以类比8421码的思想。如下图:

uTools_1645425107365.png         从十进制来讲,615的本质是什么?是6×100+1×10+5×1=615,本质是利用了不同位置的权重,8421码的原理也一样,比如27可以分解成16+8+2+1,那么只需要在存在对于权重的位置上写1,其余的写0即可。如下图:

uTools_1645425731330.png         兜兜转转这么多,十进制转二进制的这种“简单法”,其实带给我们一个思考,那就是任何正整数都可以用1,2,4,8,16,32等数字的加和来表示(而且每个数字只需要用一次)。其实也很好理解,毕竟任何正整数都可以用二进制来表示。

uTools_1645426023010.png

3.2 校验位的确定

        回顾创建海明码的初衷,我们希望海明码不仅可以指出数据是否出现了错误,还可以精准的找出出现错误的那一位,那么1个校验位肯定是不够的。假如我们由n个数据位,并且添加了k个校验位,那么校验位可以表示2的k次方种状态,一种状态用于表示数据正确,另外还需要n+k种表示有位置出错,并且指出出错的地方,即存在如下的不等关系式:

uTools_1645426796310.png

        举个例子,比如n=4,数据位为1010。为了避免空间浪费,我们取不等式成立k的最小值,即k=3。那么这三个校验位应该如何安放,是否和奇偶校验码一样,全部添加在数据位的前端或后端。并不是,海明校验码规定,选区1,2,4,8,16......的位置存放校验码,其余位置依次存放数据码,如下图:

uTools_1645427312556.png         标红的三个位置便是校验位。

3.3 校验值的确定

        海明校验码既可以采用奇校验方式。也可以采用偶校验方式。为了便于讲解,我们采用偶校验模式。现在,我们应该去想一个办法,对这7个位置进行分组,从而在偶校验的标准下确定校验位的数字。回想前面十进制转二进制的讨论,再联想我们把校验位都放在1.2.4.8.16......这些特殊的位置上,你可能就明白以下用于分组的规则了。

        首先,一共有7位,我们把1-7全部分解开,如下图右侧:

uTools_1645432121833.png

        接着,我们按照组成数有“1”的,有“2”的,有“3”的,将他们分别分成3组,并且根据偶校验的规则,计算出一位校验码,如下图:

uTools_1645432192420.png

uTools_1645432199617.png

uTools_1645432206292.png         这样便完成了整个海明校验码的构建。

四、海明校验码的纠错能力

        依然以上述校验码为例,现在为1010010,我们对上述分成的“1”,“2”,“4”三组分别进行异或运算结果都为0,当然这也很好理解,毕竟每一组中的校验位都是根据偶校验的规则选定的。假定在数据传输过程中,第三位发生了跳变,由0变成了1,会引起三组异或运算怎样的变化?如下图:

uTools_1645452334171.png         而我们将变换后的运算结果,依照他们的权重组合成一串二进制数,发现是011,翻译成十进制,刚好为3,也就是发生跳变的位置:第三位,这是巧合吗?我们再来看一个例子,如下图:

uTools_1645452563094.png         第七位发生跳变,异或运算的结果为111,翻译成十进制为7,刚好对应。这就是海明校验码神奇的地方,只有一个数字跳变的情况下,它可以实现自动纠错

五、海明校验码的纠错原理

        见证了实例之后,我们细品一下,海明校验码是如何实现这一功能的。

5.1 权重理解法

        大家首先回忆一下我们前面提到的十进制转二进制的“简单法”,任何一个正整数都可以拆分成1,2,4,8,16......数字的加和,且每个数字只需要使用1次或者0次,而我们校验位又恰巧分布在1,2,4......这些位置上,所以每个位序号的构成本质都有1,2,4......的参与,只不过有的权重是0,有的权重是1,这就导致了后续,某一个位置发生跳动,会影响权重为1的分组,比如说3这个位序号,“1”的参与权重是1,“2”的参与权重是1,而“4”的参与权重为0,所以当3位置上的数字失真时候,“1”组,“2”组的异或运算值一定会变化为1,“4”组的异或运算值却依旧为0,这也反过来证明了,1,2一定是出现问题数字的加数之一,要不它所在的组异或运算结果怎么会发生变化。

5.2 布尔运算理解法

        一定程度上,大家也可以借助布尔运算来理解。如下图:

uTools_1645453884746.png         依旧拿位置3来举例,“1”组异或运算变成1,说明位置1,3,5,7其中一个出现问题,“2”组异或运算结果变成1,从而说明只有位置3和7存在嫌疑,但是“4”组的异或结果依旧为0,所以排除7,那么便是3出现问题。大家可以对照下面这张图来进行理解:

uTools_1645454090334.png

        以上便是我分享的全部内容。如果发现某些地方表述的不太恰当,欢迎补充留言,我会积极地进行修改。