qinghuan
← 回到知识探索

计算机趣闻 / 动手实验

多加一位,为什么能发现错却找不到错?

奇偶校验只检查一条计数规则;发现规则被破坏,不代表知道该改哪一位。

展开本文目录
  1. 先动手看看
  2. 把总数凑成偶数
  3. 一次翻转改变奇偶,两次又改回来
  4. 让同一条消息遇到几种差错
  5. 报错之后,为什么不能随便修一位
  6. 记住这三点
  7. 别把例子用过头
  8. 自己试一小步
  9. 查原始资料
  10. 找一份资料继续练
开始前,会这些就够了
  • 能数出一串 0、1 中有几个 1
  • 代码需 Node.js 20 或更新版本
遇到不熟悉的词,先看这 1 个解释
二维奇偶校验
把 0 和 1 排成方格,每行、每列各补一份检查信息。以偶校验为例,先让每行和每列的 1 都是偶数个。若整张格子只有一位翻转,出问题的那一行与那一列会指向同一个位置。
打开完整术语小抄 ↗

假设两台小设备要传递四个开关状态:1010。途中某个位置从 1 变成 0,接收方仍会得到一串合法的二进制数字,光看外观不知道它已经变了。能否多发一点信息,让接收方至少发现某些差错?

翻一位报错,翻两位会怎样?

先选发送的数据,再勾选传输中翻转的位置。最后一位是自动补上的偶校验位,也可能出错。

发送方的四个数据位(改动会清除当前翻转)

加上偶校验位后,发送 10100:前四位是数据,最后一位是 0。

选择要翻转的不同位置

收到:10100,共 2 个 1。通过偶校验。

这次没有设置翻转。

为方便观察,页面同时展示发送方原文和接收结果。真实接收方通常不知道原文与翻转位置;“通过”只说明奇偶性符合规则。此模型不含删位、插位或故意篡改防护。

把总数凑成偶数

偶校验的做法是加上一位,让整段数据中 1 的总数为偶数。1010 有两个 1,因此追加 0,发送 10100。如果原数据有奇数个 1,就追加 1。检查时必须把追加的校验位也算进去;它与数据一起传输,也可能出错。

奇校验则要求总数为奇数,两端选同一套规则才能沟通。本文只用偶校验,并且只讨论固定长度内某些位置的位翻转:0 变 1,或 1 变 0。少一位、多一位、顺序错乱都不是本次模型里的差错。

一次翻转改变奇偶,两次又改回来

翻转一个位置,1 的总数不是增加一,就是减少一,奇偶性一定改变。因此,不论错的是数据位还是校验位,只要最终恰有一个位置不同,偶校验都能报错。三个、五个等奇数个不同位置翻转,也会破坏原来的奇偶性。

但两个位置翻转时,计数可能增加二、减少二或保持不变,奇偶性都不变。例如 10100 的前两个位置翻转后成为 01100,仍有两个 1,却已经是不同的数据。这里数的是最终不同的位置;同一位置翻两次会恢复,不能算成两个不同位置出错。

让同一条消息遇到几种差错

代码在原数据末尾追加校验位,再分别翻转指定位置。下标从左边的 0 开始,所以位置 4 正是最后的校验位。先预测单独翻它会怎样,再比较翻前两个位置的结果。所有输入都只含 0、1,翻转位置也都互不重复。

输出中的 error 表示没有通过偶校验,pass 只表示这条检查规则成立,不能翻译为“内容肯定正确”。程序知道原文是因为我们在制造实验;真正的接收方通常只有收到的那一串,不能拿原文逐位比较来作弊。

小实验 · javascript
function parity(bits) {
  return [...bits].reduce((total, bit) => total + Number(bit), 0) % 2;
}
function flip(bits, positions) {
  return [...bits].map((bit, i) =>
    positions.includes(i) ? String(1 - Number(bit)) : bit
  ).join("");
}
const data = "1010";
const sent = data + parity(data);
for (const positions of [[], [0], [4], [0, 1], [0, 1, 2]]) {
  const received = flip(sent, positions);
  console.log(positions.join(",") || "none", received,
    parity(received) === 0 ? "pass" : "error");
}

怎样运行:保存为 parity.js,运行 node parity.js。使用 Node.js 20 或更新版本,无依赖;示例只处理固定长度的 0、1 字符串。

运行后对照结果
none 10100 pass
0 00100 error
4 10101 error
0,1 01100 pass
0,1,2 01000 error

报错之后,为什么不能随便修一位

若接收到 10101,可能原本发送的是 10100,只是最后一位翻转;也可能原本是 00101,只是第一位翻转。这两条原文都满足偶校验,距收到的内容都只差一位。即使事先保证只错一位,接收方仍无法仅凭这一位校验信息决定选哪条原文。

因此,发现差错与纠正差错是不同能力。实际方案可以请求重发,也可以增加更多有安排的校验信息,在规定的差错范围内定位错误。它们同样需要说明范围。单个奇偶校验位既不是任意错误修复器,也不能替代针对故意篡改的真实性验证。

如果另有信息准确指出哪一位翻转,当然可以把那一位翻回去;只是位置来自额外信息,并非这条奇偶规则自己找出的。讨论一种校验能做什么时,要把这些额外条件一起说清楚,不能偷偷借用发送方才知道的原文。

记住这三点

  • 单个奇偶校验位能发现奇数个位置翻转。
  • 偶数个不同位置翻转会通过这条检查,pass 不是正确证明。
  • 知道有错,不等于知道原文或错误位置。

别把例子用过头

  • 检查时要连同校验位一起计算。
  • 本文结论限定固定长度的翻转位模型。

自己试一小步

把实验数据换成 1101,检查单点与双点翻转。

  1. 先追加偶校验位。
  2. 枚举每个单独位置和每对不同位置。
  3. 保存收到的字符串和校验结果。

做到这些,就算完成

  • 包含最后的校验位。
  • 每对位置不同且没有重复配对。
  • 解释结果,没有把通过校验当作无错证明。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。