诞生背景
总所周知:传统计算机只认识0和1的二进制码,而在底层硬件的实现中,通常以高电压表示1,低电压表示0。然而就算在科技日益发展的今天,也不能期望硬件在运行中完全按照我们所设想的方向执行,硬件偶尔也会出错。
那么如何保证数据在传送过程中正确无误,既得到的数据就是真实的数据,而不用担心由于硬件高负债,错误的把其中一个高电压输送成了低电压,使得到的一串二进制里有一位与它本来的意思相反。那么如何避免这个情况呢?
- 一是提高硬件的可靠性,使故障率降到最低,但不能确保故障率降为0;
- 二是使所传输的数据自身能支持被校验的能力,并可配合程序对数据进行:发现错误 -> 纠正错误;
通过上面的分析,得出了两种解决方案,很明显,第二种方案是必选的,因为第一种无法完全解决问题。对于第二种方案,在计算机系统中有一个统一的名词校验码。校验码有多种,有奇偶校验码、循环冗余校验码等等,下面我们先介绍下奇偶校验码,看看他能否解决我们所说的问题;
奇偶校验码
奇偶校验是一种简单有效地校验方法,通常最普通的奇偶校验是在编码中增加一位校验位,校验位记录了编码中1的个数是否为奇数或偶数,当发生错误时,既编码中的1变成0或0变成1,则该编码中的奇偶性则发生了变化,从而可以发现错误。
例如:当前有一个4位二进制数:1011,我们在编码的最后面在增加一位校验位,使得原始二进制数变更为10111 (因源数据1011中有3个1,1的个位为奇数,所以校验位也为1,反之则为0)
此时10111中任意一位编码发生错误被取反,则我们可以马上发现。如10111发生错误时,变成了11111,此时编码中非校验位1的个数为偶数,但是校验位却告诉我们原始数据中1的个数为奇数,数据错误显而易见。
虽然一般的奇偶校验具备发现错误的能力,但他还是存在诸多缺陷,如:对编码中有多处发生错误的场景无能为力,以及不具备纠正错误的能力。
对编码中有多处发生错误的场景无能为力:既在刚刚的编码中,当源编码10111的第一位和第二位都发生错误, 变更为01111时,我们是无法发现这条数据是错误的。
无纠错能力:既在刚刚的编码中,我们模拟错误时把第二位的0变更为了1,因为这个错误的产生是我们操作的,所以我们知道在哪一位发生了错误,但是在实际环境中,我们是不可能知道硬件是在执行哪一位出错的。
通过上面的分析,一般的奇偶校验不太能解决我们的实际问题,因为这两个缺陷,emmmm影响还蛮大的。那么有没有比它更好的校验码存在呢。海明码随之诞生而来
海明码
海明码也是通过在编码上增加校验位来实现校验,但是在奇偶校验码的基础之上,增加了对编码的长度的敏感。
校验位长度和校验位所处的位置
也就是说海明码的校验位有多少位不是固定的,而是根据要校验的编码有多长而定;
假设编码长度是n,校验位有k位,则他们的关系公式为:2ᵏ -1 ≥ n + k ,当给海明码制定校验位长度时,应满足此公式。
既然海明码有多位校验位,那么他的这些校验位分别处于哪些位置呢,是全部放在编码的右边或左边,还是根据什么规则来控制校验位在哪里呢?
i代表这是第几位的校验位,则他所处的位置应该处于编码的2ⁱ⁻¹位置
以编码01101001为例:编码长度为8,因为8是2的3次幂,我们把3套进公式:2ᵏ -1 ≥ n + k ,得到2³ - 1 ≥ 8 + 3,发现公式不成立。此时把3 + 1 换成4来试试公式,2⁴ - 1 ≥ 8 + 4公式成立。得出8位的编码一共需要4位校验码。这4位校验码分别处于2¹⁻¹、2²⁻¹、2³⁻¹、2⁴⁻¹的位置,运算出来的结果分别是:1、2、4、8。
假设这4位校验位分别为P₁、P₂、P₃、P₄,原始编码为:D₀、D₁、D₂、D₃、D₄、D₅、D₆、D₇,他们的组合体统称为H,那的排列应该是:
H₁₂ H₁₁ H₁₀ H₉ H₈ H₇ H₆ H₅ H₄ H₃ H₂ H₁
D₇ D₆ D₅ D₄ P₄ D₃ D₂ D₁ P₃ D₀ P₂ P₁
0 1 1 0 ? 1 0 0 ? 1 ? ?
校验位校验哪些数据
这个目前在网上主要有两种思路来理解,一种是从他的实际规律所观察到行为来讲、一种是从他的实现方式来讲,我们在这里都提一下。暂且称呼他们为体现形式和实现形式。
体现形式
还是上面的例子,现有校验位P₁、P₂、P₃、P₄,那么他们每个校验位分别校验哪些数据呢。先给出结果,然后再根据结果找规律。
P₁校验:P₁、D₀、D₁、D₃、D₄、D₆
P₂校验:P₂、D₀、D₂、D₃、D₅、D₆
P₃校验:P₃、D₁、D₂、D₃、D₇
P₄校验:P₄、D₄、D₅、D₆、D₇
i代表这是第几位的校验位,它校验规则是:从当前位数起,校验2ⁱ⁻¹位,然后跳过2ⁱ⁻¹位,再校验2ⁱ⁻¹位,再跳过2ⁱ⁻¹位......以此循环到数据末尾
把P₁带入上面规则就是:从当前位数起,校验1位,然后跳过1位.......
把P₃带入上面规则就是:从当前位数起,校验4位,然后跳过4位.......
实现形式
根据上面的公式,知道校验码位于2ⁱ⁻¹位置,也就是P₁、P₂、P₃、P₄,他们分别在下标1、2、4、8的位置。先取出他们的二进制
1 | 2 | 4 | 8 |
0001 | 0010 | 0100 | 1000 |
把二进制中他们的0换成*通配符
1 | 2 | 4 | 8 |
***1 | **1* | *1** | 1*** |
我们将12->1依次与上面的通配表进行匹配
1 | 2 | 4 | 8 |
***1 | **1* | *1** | 1*** |
001(1) | 0010(2) | 0100(4) | 1000(8) |
011(3) | 0011(3) | 0101(5) | 1001(9) |
101(5) | 0110(6) | 0110(6) | 1010(10) |
111(7) | 0111(7) | 0111(7) | 1011(11) |
1001(9) | 1010(10) | 1100(12) | 1100(12) |
1011(11) | 1011(11) |
因此我们可以确定
P₁ 负责 1 3 5 7 9 11 位数的校验
P₂ 负责 2 3 6 7 10 11 位数的校验
P₃ 负责 4 5 6 7 12 位数的校验
P₄ 负责 8 9 10 11 12 位数的校验
校验位取值
取值采用异或运算,相异为1,相同为0。也可通过计数1的个数是否为偶数来觉得
H₁₂ H₁₁ H₁₀ H₉ H₈ H₇ H₆ H₅ H₄ H₃ H₂ H₁
D₇ D₆ D₅ D₄ P₄ D₃ D₂ D₁ P₃ D₀ P₂ P₁
0 1 1 0 ? 1 0 0 ? 1 ? ?
P₁校验:P₁、D₀、D₁、D₃、D₄、D₆
P₂校验:P₂、D₀、D₂、D₃、D₅、D₆
P₃校验:P₃、D₁、D₂、D₃、D₇
P₄校验:P₄、D₄、D₅、D₆、D₇
得出
H₁₂ H₁₁ H₁₀ H₉ H₈ H₇ H₆ H₅ H₄ H₃ H₂ H₁
D₇ D₆ D₅ D₄ P₄ D₃ D₂ D₁ P₃ D₀ P₂ P₁
0 1 1 0 0 1 0 0 1 1 0 1
校验位纠错
每个校验位校验一组数据,当发生错误时。其实就是在多个集合取并集差集的操作。
例如上面的数据,当数据D₁发生错误时,会导致P₁和P₃校验不通过。P₁和P₃的并集是D₁和D₃,但是D₃也是P₂的子集,P₂没发生校验不通过。结果就只能是D₁发生了错误
注:海明码只能检测出2位错,纠1位错