当曲高和寡不是一个结果的时候,它往往会成为一种错觉,……,若你的思考真的精妙到了只有你自己能理解,那你岂不是更应该将你精妙的思考分享给他人?

汉明码、海明校验码(Hamming Code)

基础知识

  1. 码距:又叫海明距离,是在信息编码中,两个编码之间对应位上编码不同的位数。例如编码100110和010101,第1、2、5、6位都不相同,所以这两个编码的码距就是4,并且可以通过异或的方式求出(异或后计算零的个数)

  2. 奇偶校验(Parity Check):一种校验代码传输正确性的方法,分为奇校验和偶校验两种方式,目的都是在一字节(8位二进制数)后面加上一个校验位(0或1),奇校验下前面8位和校验码的1的个数应为奇数个,偶校验为偶数个。公式表示为:

    \[奇校验:G=\overline{C\oplus X_1\oplus X_2\oplus X_3\oplus ... \oplus X_n}\\ 偶校验:G=C\oplus X_1\oplus X_2\oplus X_3\oplus ... \oplus X_n\\ G等于0表示数据正常,否则表示出错 \]

    在实际中奇偶校验的选择是事先选择的。

    奇偶校验的特点

    1. 编码检错简单
    2. 编码效率高
    3. 不能检测偶数位错误,无错结论不可靠,是一种错误检测码
    4. 也没法识别错误,更不能纠正错误,出错只能重新传

汉明码/海明校验码

海明码的构成方法是在数据位之间的确定位置上插入k个校验位,通过扩大码距来实现检错和纠错。

汉明码跟其它的错误校验码类似,也利用了奇偶校验位的概念。不过与奇偶校验码不同的是,它并不是指定长度字节后面加一位,而是通过计算关系:

\[2^k-1\ge n+k(在指定n的情况下,找到满足式子的最小的k) \]

计算出指定数据位对应的校验位长度。其中n为数据位,k为校验位长度。

计算

设数据位是8位,那么通过式子\(2^4-1=15>8+4=12\),则校验位为4位

设信息位为:D7,D6,D5,D4,D3,D2,D1,D0,校验位P3,P2,P1,P0,先计算校验位在总共12位的海明码中占据的位数:

  • \(P0=2^0=1\)
  • \(P1=2^1=2\)
  • \(P2=2^2=4\)
  • \(P3=2^3=8\)

所以校验位P3,P2,P1,P0分别占据第8、4、2、1位,而信息码从左到右占据剩下的位数,如表:

位数 12 11 10 9 8 7 6 5 4 3 2 1
信息位 D7 D6 D5 D4 D3 D2 D1 D0
校验位 P3 P2 P1 P0

注:这个表很多资料中其实是左右倒过来的,如果是倒过来的话就不用最后在倒置了

而信息位与校验位的关系为:信息位的位数=位数相加等于信息位位数的校验位组。例如D7的位数为12,所以D7的校验位组为P3和P2。

信息位 信息位检验计算 校验位组
D7 12=8+4 P3,P2
D6 11=8+2+1 P3,P1,P0
D5 10=8+2 P3,P1
D4 9=8+1 P3,P0
D3 7=4+2+1 P2,P1,P0
D2 6=4+2 P2,P1
D1 5=4+1 P2,P0
D0 3=2+1 P1,P0

所以,P0参与了D0,D1,D3,D4,D6的检验,其他由此类推:

\(P0=D0\oplus D1\oplus D3\oplus D4\oplus D6\)
\(P1=D0\oplus D2\oplus D3\oplus D5\oplus D6\)
\(P2=D1\oplus D2\oplus D3\oplus D7\)
\(P3=D4\oplus D5\oplus D6\oplus D7\)

注意:海明码是由高位到低位进行排序(高位先放,低位后方),所以得到的海明码是:P0 P1 D0 P2 D1 D2 D3 P3 D4 D5 D6 D7

而校验的方式则是算出信息的校验码,然后与得到的校验码对应,相同标0,不同标1(让01串异或成原来的样子),得到错误的位置为\(P3^*P2^*P1^*P0^*\)\(Pn^*\)是标记)

python代码实现校验(这代码摸了快三天,代码水平还是得提高)

# -*- coding: utf-8 -*-
"""
Created on Fri Dec  3 21:59:38 2021
海明码解码纠错(默认只有一个错误)
@author: 01am
"""
#返回将列表里面的元素全部异或的值
def xor1(lista):
    j=lista[0]
    for i in range(1,len(lista)):
        j=j^lista[i]
    return j

a = input("请输入海明码:")
len_a = len(a)
k=0

#确定k的值
for i in range(1,len_a):
    tmp = pow(2,i)
    if tmp > len_a :
        k=i
        break

#循环创建校验位变量和校验位对应的数据位列表
for i in range(k):
    exec('P'+str(i)+'='+str(a[pow(2,i)-1]))
    exec('D'+str(i)+'=[]')

#循环创建数据位变量并确定是否与校验位有关
for i in range(len_a):
    #如果按照位数的二进制情况下查找1只有1个的情况下,那么一定是校验位
    if bin(i+1).count('1')==1:#加一是为了解决range从0开始而海明码从1开始的矛盾
        continue
    else:
        tmp1=bin(i+1)[-1:1:-1]#将二进制数翻转并且去掉0b,方便后面循环寻找相关的校验位
        for j in range(len(tmp1)):
            if tmp1[j]=='1' :#这里无需加一,因为校验位是从0开始的
                exec('D'+str(j)+'.append('+str(a[i])+')')

#新计算的纠错位和原校验位对比后的标志不准确
#判断校验位和计算出来的校验位是否一致
K=''
for i in range(k):
    exec('tmp2 = D'+str(i))  #这里的问题,不能写成tmp2 = exec('D'+str(i)),这样会让tmp2成为NoneType
    exec('tmp3 = P'+str(i))
    tmp4 = xor1(tmp2)  #这里可能会提示有变量未定义,不用理
    if tmp4 == tmp3 :  #这里可能会提示有变量未定义,不用理
        K+='0'
    else:
        K+='1'
tmp5 = K[::-1]#校验位的位置,如果全为零就不存在

#判断校验码是否存在,存在就把对应的位置修改
a=list(a)
if tmp5.find('1')!=-1:
    int1 = int(tmp5,2)-1
    a[int1]=str(int(a[int1])^1)
    print("已经自动纠错一位\n")

#输出不带校验码的字符串
b=''
c=[pow(2,i) for i in range(k)]
for i in range(len_a):
    if i+1 not in c:
        b+=a[i]
a=''.join(a)
print(a)
print(b)

参考:

  1. 海明码的计算步骤
  2. 百度百科-奇偶校验
  3. 百度百科-海明校验码
posted @ 2021-12-06 08:58  01am  阅读(2261)  评论(0编辑  收藏  举报