CRC 校验码计算时为何要补 0 及相关内容的简单探索

提示:这篇文章也在作者的博客上发布,协议为 CC BY-NC 4.0。

沈锎

看上去是没啥用的东西,只是为了满足好奇心 @(o・ェ・)@


目录

  • Sec 1:为何计算校验码需要在数据后补 0?
  • Sec 2:为何校验过程恰好整除?
  • Sec 3:如何利用校验码进行纠错?
  • Sec 4:校验码的并行生成
  • Sec 5:Logisim 中 CRC 校验码的生成与纠错
  • 参考文献

在 P0 课下中有一道题目,要求我们使用 Logisim 搭建一个除数为 4 位,原数据帧为 8 位的 CRC 校验码计算电路,题目中有提到这样一句话:

因为 B 是 4 位二进制数,我们需要在 A 的后面补上 3 个 0

其中,B 是选定的除数,A 是 8 位原数据。有同学在答疑群中询问这里的因果关系,得知是 CRC 算法规定的的计算步骤,但为何 CRC 算法需要在原数据后面补 0 计算呢?越想越搞不懂,我便上网找了一些资料,并结合自己的推导与概括,和大家分享一下省流版的结论。 (o_O)

约定:此帖所有计算,除非特殊说明,均为模二运算;所有除法,除特殊说明,均为模二带余除法,用 \(r\) 表示余数;所有数字,除特殊说明,均为二进制无符号整数

Sec 1:为何计算校验码需要在数据后补 0?

先说结论:为了方便校验以及纠错过程[1]。

既然是校验码,肯定是为了防止数据在通信传输过程中出现偏差而诞生的,自然,我们生成的校验码也需要具备校验的能力。

假设通信双方选定一个除数 \(B=1101\),传输原数据 \(A=1011\ 0010\)。如果不补 0,则有 \(A \divsymbol B = 11001, r = 111\),将余数拼接到原数据帧后,得到 11 位的输出 \(C=1011\ 0010\ 111\),接收方接收到数据后,需要将其拆分成 \(8+3\) 位,使用约定好的除数,计算前 8 位的余数,与后 3 位进行比较,从而检验数据传输的可靠与否。

但是,这个过程还是相对繁琐的,也无法判断出错的位置(比如接收到 \(1011\ 0011\ 111\),余数算下来是 \(110\),你能知道是数据的最低位传输错误,还是余数的最低位传输错误吗),因此还需要改进。

关于为何这样计算校验码不行,资料 [1] 中仅提到 “过程繁琐”,但我在后续的探求中,觉得这一个观点过于单薄,遂补充了 “无法纠错” 这一原因。

于是,科学家们想出了一种解决方法,就是题目中所描述的那样,在 \(A\) 后补 0,个数为 \(B\) 的位数减去 1,得到 \(A_1=1011\ 0010\ 000\),此时有 \(A_1 \divsymbol B = 11001101, r=001\),进行拼接,得到 \(C=1011\ 0010\ 001\),接收方在接收数据后,计算 \(C \divsymbol B\) 的余数,如果余数是 0,则说明传输过程可靠。

Sec 2:为何校验过程恰好整除?

上述所说并非巧合,补 0 的逻辑也在这里呈现[2]。

不过首先,得引入一个叫做 生成多项式 的东西,对于一个二进制数 \(\overline{d_{n-1}d_{n-2}\cdots d_1d_0}\),其中 \(d_i=0|1\)\(d_{n-1} \neq 0\),有唯一的 \(n-1\) 次多项式与之对应:

\[D(x) = d_{n-1}x^{n-1} + d_{n-2}x^{n-2} + \cdots + d_1x + d_0\]

反之亦然。

对于 \(n\) 位的原始数据 \(A=\overline{d_{n-1}d_{n-2}\cdots d_1d_0}\),以及 \(k\) 位的除数 \(B=\overline{g_{k-1}g_{k-2}\cdots g_1g_0}\),它们的生成多项式分别为:

\[D(x) = d_{n-1}x^{n-1} + d_{n-2}x^{n-2} + \cdots + d_1x + d_0 \qquad G(x) = g_{k-1}x^{k-1} + g_{k-2}x^{k-2} + \cdots + g_1x + g_0\]

\(D(x)\) 两边同时乘以 \(x^{k-1}\),即添了 \(k-1\) 个 0

\[x^{k-1} \cdot D(x) = d_{n-1}x^{n+k-2} + d_{n-2}x^{n+k-3} + \cdots + d_1x^k + d_0x^{k-1}\]

对其作多项式的带余除法,得到次数最高为 \(k-2\) 次的余数多项式 \(R(x)\) 与商多项式 \(Q(x)\)

\[x^{k-1} \cdot D(x) = Q(x)G(x) + R(x)\]

两边同时加上 \(R(x)\),因为是模二运算,有 \(R(x) + R(x) = 0\)

\[x^{k-1} \cdot D(x) + R(x) = Q(x)G(x)\]

因为二进制数与生成多项式的一一对应关系,有 \(R(x) = r_{k-2}x^{k-2} + \cdots + r_1x + r_0\),于是:

\[Q(x)G(x) = d_{n-1}x^{n+k-2} + \cdots + d_1x^k + d_0x^{k-1} + r_{k-2}x^{k-2} + \cdots + r_1x + r_0\]

其对应的二进制数为:

\[C = \overline{d_{n-1}d_{n-2}\cdots d_1d_0r_{k-2}r_{k-3}\cdots r_1r_0}\]

这不是巧了不是,恰好是原数据拼上余数!而且它的生成多项式还恰好等于 \(Q(x)G(x)\),这也意味着整除!(◆゜∀゜)b

Sec 3:如何利用校验码进行纠错?

通过一些科普性质的文章,我了解到,CRC 校验码可以对一位错的情况就行纠错(也就是,不仅可以判断数据传输的可靠性,还可以把出错的那一位揪出来),其方法是,通过余数(这里的余数指接收方对接收到的数据(包含数据位与校验位)进行模二除法得到的余数)与出错位一一对应的关系查表

那其中究竟是怎样的一个对应关系呢?

以下过程并不保真,请注意甄别。

首先假设校验位不会出错,且在数据位中有一位错。假设 \(d_s\) 在传输过程中出现差错,则有接收到的 \(C' = \overline{d_{n-1}\cdots d_s' \cdots d_0r_{k-2}r_{k-3}\cdots r_1r_0}\),其中 \(d_s \oplus d_s' = 1\).

考虑它们的生成多项式与模二加法的性质,则有:

\[C'(x) = C(x) + x^{s+k-1} = Q'(x)G(x) + R'(x)\]

\(R'(x)\) 是校验时得到的余数多项式。又因为 \(C(x) = Q(x)G(x)\),所以:

\[x^{s+k-1} = (Q(x) + Q'(x))G(x) + R'(x)\]

即二进制数 \(\overline{100\cdots0}\)\(s+k-1\) 个 0)除以 \(B\) 的余数,就是校验时得到的余数!

拿题目的背景试一试吧!有 \(k=4\),数据位数为 8.

数据位出错位数 \(s\) \(s+k-1\) 余数 备注
0 3 \(101\) \(1000 \divsymbol 1101\)
1 4 \(111\) \(10000 \divsymbol 1101\)
2 5 \(011\) \(100000 \divsymbol 1101\)
3 6 \(110\) \(\cdots\)
4 7 \(001\) \(\cdots\)
5 8 \(010\) \(\cdots\)
6 9 \(100\) \(\cdots\)

但是,第 7 位却让我心凉了半截:余数是 \(101\)!这还怎么检错呢?

正当我对上述过程产生怀疑,并准备另找出路时,我突然意识到,余数总共就 3 位,最多 8 种状态,还要把全 0 留给正确的情况,怎么可能完美地纠错呢?

换一句话说,题目给定情况下,余数位数过少

那还是采用一种主流的位数来讨论吧,数据位数为 4,除数位数也为 4,总数据位数为 \(4 + 3 = 7\).假设取定除数 \(B=1101\),并且含校验位数据只有一位错,则出错位数与余数的对应关系如下:

数据位出错位数 \(s\) 余数 备注
0 \(001\) \(1 \divsymbol 1101\)
1 \(010\) \(10 \divsymbol 1101\)
2 \(100\) \(100 \divsymbol 1101\)
3 \(101\) \(\cdots\)
4 \(111\) \(\cdots\)
5 \(011\) \(\cdots\)
6 \(110\) \(\cdots\)

这样,对于接收方来说,收到 7 位数据 \(C'\),计算 \(C' \divsymbol B\) 的余数,若余数为 0,则表示没有错误,否则,出错位可按上表查询。

这是一位错的情况,对于两位错,CRC 也同样可以判断(也许需要增加一个奇偶校验位,这里我没有深究),但是无法纠错。对于三位错,CRC 就无能为力了,当然,这种情况极其罕见。

Sec 4:校验码的并行生成

通过一些科普性质的文章,我也了解到,CRC 校验码的生成还有一种并行的方法,就是将若干个数异或起来得到余数。

假定 \(A=1011\)\(B=1101\),给定一个原始串 \(C_0=0000\ 000\),这是 CRC 校验成功的。现在,使其逐位接近 \(A\),有第 6、4、3 位的差异,那么将这些位上对应的余数异或起来,可以得到 \(C = 1011\ 100\),这就是生成好的 CRC 校验码。

我觉得整个过程可以理解为纠了三次错,从 \(0000\ 000\) 纠正到了 \(1011\ 100\).

那么,校验的过程是不是也可以并行执行呢?应该也是可以的,不赘言。

Sec 5:Logisim 中 CRC 校验码的生成与纠错

总要写一点和课程相关的内容的吧 ᕕ( ᐛ )ᕗ

按照上面的思路,对于 \(B=1101\) 的情况,结合余数表,可以很容易画出如下的电路:

crcgen.png

此外,我还弄了一个简单的测试电路,将其结果与使用模二除法得到的结果进行比较,如下分别是数据设置与比较电路:

crcdata.png

crccheck.png

对 CRC 校验码进行一位纠错也很简单,这里采用除法简化过程(好像有个类似的并行的纠错电路,但是研究不动了 (.. )…)

如下是扰动设置与余数表的生成电路:

crcr.png

并通过 7 位除法得到校验余数:

crccorrect.png

这里并没有真正得到纠错后的数据,但只要将得到的余数与余数表比较即可!


什么?你在说 P0 课下那道题?拜托,那个题很不一样的好吗,算 8 次除法得了,而且除数还不确定 ……

参考文献

[1] 思否论坛.不要跑,CRC没这么难!(简单易懂的CRC原理阐述)[EB/OL].(2019-02-02)[2023-09-28].https://segmentfault.com/a/1190000018094567存档

[2] 仇晓涛.通用并行CRC计算方法及FPGA实现[J].无线互联科技,2023,19(02):115-117+168.


doc(助教)

cscore 的 markdown 渲染使用了 Mathjax 并配置使用了 tex/physics 输入组件,该组件将\div的宏定义覆写为哈密顿算符,你可以使用\divsymbol来表示除号\(\divsymbol\)


沈锎 回复 doc(助教)

谢谢助教,已经改正 ヾ(^▽^*)))

0%