海明码是一种纠错编码,也就是发送海明码给接收端后,如果传输过程出错,接收端根据收到的码的特征,可以判断出是否出错,并且知道如何纠正出错的位(bit)。
接下来介绍给出一段信息码后,如何计算出它的海明码。
假设信息码为 1010
分 4 个步骤计算其海明码
1、计算需要几位校验码
根据公式 n + k ≤ 2 k − 1 n + k \le 2^k -1 n+k≤2k−1 计算,其中【k】为校验码位数(k取满足公式的最小值),【n】为信息码的位数。
因此1010
对应的校验码的位数 k=3,则海明码总共 4+3 = 7 bit
可以假设最后计算结果海明码为 H 7 H 6 H 5 H 4 H 3 H 2 H 1 H_7H_6H_5H_4H_3H_2H_1 H7H6H5H4H3H2H1
2、计算校验码放的位置
也就是说,得到校验码的位数后,该放在第几位,第一位、第二位、还是第三位…
假设用 P 3 P 2 P 1 P_3P_2P_1 P3P2P1代表 3bit 的校验码, D 4 D 3 D 2 D 1 D_4D_3D_2D_1 D4D3D2D1代表 4bit 的信息码
则 P i P_i Pi的位置为 2 i − 1 2^{i-1} 2i−1,也就是:
然后信息位按从高到低位的顺序填补剩余空位,因此海明码的结构:
H7 | H6 | H5 | H4 | H3 | H2 | H1 |
---|---|---|---|---|---|---|
D4 | D3 | D2 | P3 | D1 | P2 | P1 |
3、确定每个信息位分别由哪几个校验位负责校验
为了确保信息位中每一位都能得到“照顾”,需要为每一个信息位都分配几个校验位来校验,该信息位出错后可以根据对应的校验位推测出原来是什么。
011
,二进制3中的1
位于哪几位,就由哪几位P负责,因此D1是由【P2P1】负责校验101
,因此D2由【P3P1】负责校验110
,因此D3由【P3P2】负责校验111
,因此D3由【P3P2P1】负责校验注意:只需要计算信息位的校验码,校验位不需要再计算校验码,传输过程假设校验位
4、计算校验位的具体值
即确定P3、P2、P1具体是1还是0
信息码不用确定:
D4 D3 D2 D1 = 1 0 1 0
因此,最终的汉明码为 D4 D3 D2 P3 D1 P2 P1
= 1010010
每个校验组分别利用校验位和参与形成该校验位的信息位进行【奇偶校验】构成k个方程:
{ S 1 = P 1 ⊕ D 1 ⊕ D 2 ⊕ D 4 S 2 = P 2 ⊕ D 1 ⊕ D 3 ⊕ D 4 S 3 = P 3 ⊕ D 2 ⊕ D 3 ⊕ D 4 \left\{\begin{matrix} S_1 & = & P_1\oplus D_1\oplus D_2\oplus D_4\\ S_2 & = & P_2\oplus D_1\oplus D_3\oplus D_4\\ S_3 & = & P_3\oplus D_2\oplus D_3\oplus D_4\\ \end{matrix}\right. ⎩ ⎨ ⎧S1S2S3===P1⊕D1⊕D2⊕D4P2⊕D1⊕D3⊕D4P3⊕D2⊕D3⊕D4
如果 S3 S2 S1 = 000
时说明无差错,否则说明出错,出错的位就是 S3 S2 S1
表示的值。
如 001
表示第一位(即H1)出错,110
是第6位(H6)出错,纠正只需要把错的位取反(0变1,1变0)即可
运行截图
#include
#include
#include
#include
#include
#include
using namespace std;
bool hasElem(vector<int> arr, int a); //判断数组arr中是否含有元素a
void testRandom(); // 随机发送几个数字测试
void testString(string s); // 发送字符串测试
int getRandom(int a, int b); // 返回[a, b)之间的随机数
class Sender
{
public:
int msgInput;
int n, k;
int hammingCode;
bool tranErr;
Sender(int msgInput);
void setInput(int msg);
int getK();
int getHammingCode();
bool transmit(); // 返回传输过程是否出错
void clear();
~Sender();
};
class Receiver
{
public:
int n; // 信息码位数,提前协商好
int msgRecv;
int msgRecvRect; // 检测到错误修正后的数据
int msgDecode; // 解码后返回的数据, 即发送者输入的值(未编码的值)
bool checkErr; // 是否检测到传输发生的错误
Receiver(int msgRecv, int n);
void setInput(int msgRecv, int n);
void clear();
bool recvCheck();
};
Sender::Sender(int msgInput)
{
this->msgInput = msgInput;
this->tranErr = 0;
this->n = 0;
this->k = 0;
this->hammingCode = 0;
}
Receiver::Receiver(int msgRecv, int n)
{
this->msgRecv = msgRecv;
this->msgRecvRect = 0;
this->msgDecode = 0;
this->n = n;
this->checkErr = 0;
}
int main()
{
srand(unsigned(time(NULL)));
testRandom();
// testString("Hello-World!");
return 0;
}
void testRandom()
{
int msg = 0;
Sender *sender = new Sender(0);
Receiver *receiver = new Receiver(0, 0);
// scanf("%d", &msg);
for (int i = 0; i < 10; i++)
{
printf("\n\e[1;34m==============START[%d]===============\e[0m\nInput Data: ", i);
msg = getRandom(0, 100);
sender->setInput(msg); // 这是设置输入的数据
printf("%d\n", sender->msgInput);
sender->getK();
sender->getHammingCode();
printf("[The hamming code is: %d]\n", sender->hammingCode);
sender->transmit();
if (sender->tranErr)
printf("\e[1;31m[Found an error when transmit...]\e[0m\n");
else
printf("\e[1;32m[No error when transmit...]\e[0m\n");
// 开始接收
receiver->setInput(sender->hammingCode, sender->n);
receiver->recvCheck();
if (receiver->checkErr)
printf("\e[1;31m[Check an error...]\e[0m\n");
else
printf("\e[1;32m[Checking not find errors]\e[0m\n");
printf("Msg receive before rectify is: %d \n", receiver->msgRecv);
printf("Msg receive after rectify is: %d \n", receiver->msgRecvRect);
printf("Msg receive after decode is: %d \n", receiver->msgDecode);
printf("\e[1;34m===============END[%d]===============\e[0m\n", i);
sender->clear();
receiver->clear();
// delete sender;
// delete receiver;
}
}
void testString(string s){
Sender *sender = new Sender(0);
Receiver *receiver = new Receiver(0, 0);
string result = "";
for (int i = 0; i < s.size(); i++)
{
sender->setInput(s[i]);
sender->getK();
sender->getHammingCode();
sender->transmit();
receiver->setInput(sender->hammingCode, sender->n);
receiver->recvCheck();
result.push_back(char(receiver->msgDecode));
sender->clear();
receiver->clear();
}
printf("==================\n[==%s==]\n==================", result.c_str());
}
bool hasElem(vector<int> arr, int a)
{
for (int i = 0; i < arr.size(); i++)
{
if (arr[i] == a)
return true;
}
return false;
}
int getRandom(int a, int b)
{
int r = a + rand() % (b - a);
return r;
}
void Sender::setInput(int msg){
this->msgInput = msg;
}
// step1,求k,参数是需要传输的信息(十进制即可)
int Sender::getK()
{
int msg = msgInput;
while (msg)
{
msg >>= 1;
n++;
}
while (n + k > ((1 << k) - 1))
{
k++;
}
}
// step2/3/4, 求校验码
// 参数是msg以及上一步的k
int Sender::getHammingCode()
{
int msg = msgInput;
int ans = 0; // 存放最终结果
vector<int> p; // 存放校验码位置,左低右高
vector<int> d; // 存放信息码位置,左低右高
for (int i = 0; i < k; i++)
{
p.push_back(1 << i);
}
for (int i = 1; i <= n + k; i++)
{
if (!hasElem(p, i))
{
d.push_back(i);
}
}
for (int i = 0; i < d.size(); i++)
{
// printf("%d ", d[i]);
ans += (msg & 1) << (d[i] - 1);
msg >>= 1;
}
for (int i = 0; i < p.size(); i++)
{
int x = 0;
for (int j = 0; j < d.size(); j++)
{
if ((d[j] >> i) & 1 != 0)
x = x ^ ((ans >> (d[j] - 1)) & 1);
}
if (x == 1)
ans += (1 << (p[i] - 1));
}
hammingCode = ans;
return ans;
}
// 传输过程某一位出错,海明码只能校验一位出错情况,x为传输位数
bool Sender::transmit()
{
int msgSend = hammingCode;
bool f = 0; //某次传输是(1)否(0)出错
int mistakeBit = 0; // 出错的位,从0开始
f = rand() & 1; // 某一时刻是否出错
if (f)
{
mistakeBit = rand() % (n + k);
msgSend ^= (1 << mistakeBit); // 出错位与1异或(出错位是1,则变为0;0则变为1)
// printf("===the bit error is: %d \n", mistakeBit+1);
}
// printf("TEST------>msg: %d========\n", msgSend);
tranErr = f;
hammingCode = msgSend;
return f;
}
void Receiver::setInput(int msg, int n){
this->msgRecv = msg;
this->n = n;
}
// n是收发双方约定的位数
bool Receiver::recvCheck()
{
int msgR = msgRecv;
int k = 0; // 接收方计算k
while (n + k > ((1 << k) - 1))
k++;
vector<int> p; // 存放校验码位置,左低右高
vector<int> d; // 存放信息码位置,左低右高
int s = 0; // 校验子,用于表示哪1位出错了,0表示没有出错
checkErr = 0;
for (int i = 0; i < k; i++)
{
p.push_back(1 << i);
}
for (int i = 1; i <= n + k; i++)
{
if (!hasElem(p, i))
{
d.push_back(i);
}
}
for (int i = 0; i < k; i++)
{
int x = (msgR >> (p[i] - 1)) & 1;
for (int j = 0; j < n; j++)
{
if ((d[j] >> i) & 1)
x ^= ((msgR >> (d[j] - 1)) & 1);
}
s += (x << i);
}
// 下面进行错误位纠正
if (s != 0)
{
msgRecvRect = msgR ^ (1 << (s - 1));
checkErr = 1;
}
else
{
msgRecvRect = msgR;
}
int tmp = msgRecvRect, tt = 0;
for (int i = 1; i <= n + k; i++)
{
if (hasElem(p, i))
{
tmp >>= 1;
tt++;
}
else
{
msgDecode += ((tmp >> (i - tt - 1) & 1) << (i - tt - 1));
}
}
return checkErr;
}
void Sender::clear(){
this->hammingCode=0;
this->k = 0;
this->msgInput = 0;
this->n = 0;
this->tranErr = 0;
}
void Receiver::clear() {
this->msgRecv = 0;
this->n = 0;
this->msgRecvRect = 0;
this->msgDecode = 0;
this->checkErr = 0;
}