基礎知識
-
碼距:又叫海明距離,是在信息編碼中,兩個編碼之間對應位上編碼不同的位數。例如編碼100110和010101,第1、2、5、6位都不相同,所以這兩個編碼的碼距就是4,並且可以通過異或的方式求出(異或后計算零的個數)
-
奇偶校驗(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表示數據正常,否則表示出錯 \]在實際中奇偶校驗的選擇是事先選擇的。
奇偶校驗的特點
- 編碼檢錯簡單
- 編碼效率高
- 不能檢測偶數位錯誤,無錯結論不可靠,是一種錯誤檢測碼
- 也沒法識別錯誤,更不能糾正錯誤,出錯只能重新傳
漢明碼/海明校驗碼
海明碼的構成方法是在數據位之間的確定位置上插入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)
參考:
