CRC算法
1、 错误检测
错误检测的目的是使接收者能够检测它从一个吵杂的信首接收到的消息是否被破坏。因此发送者构建了一个值(称为校验和)并把它附加到信息中,这个值是信息的函数。接收者可以用相同的函数算出它接收到信息的校验和,并与附加的校验和比较看接收到信息是否正确。例如,如果我们选择了一个校验和函数,这个函数简单的以模256计数信息中各字节的和,然后就会像正面一样。所有的数都是十进制。
Message : 6 23 4
Message with checksum : 6 23 4 33
Message after transmission : 6 27 4 33
上面信息第二个字节被破坏,它的值被信道从23变到27。而接收者可以通过把传输过来的校验和(33)与计算的校验和(37: 6+27+4)比较检测到错误。如果校验和本身遭到破坏,一个正确传输的信息会被标识为被破坏的。不过,这个错误是安全的。如果信息或校验和被以某种方式破坏,这种方式导致内部一致的传输,那么这个错误是危险的。不幸的是这个可能性是完全不可避免的,所能做的最好的事情就是通过在校验和中增加信息量来减少这种可能性(如,把校验和从一字节加宽到两字节)
2、 复杂性需求
通过上面的例子,我们看到怎样用一个简单的校验和算法检测一个被破坏的信息,这个算法就是简单的将信息中的字节进行模256相加:
Message : 6 23 4
Message with checksum : 6 23 4 33
Message after transmission : 6 27 4 37
这个算法的问题就是它太简单了。如果有大量随机的破坏发生,那这些破坏就有1/256的机会不会被检测到,例如:
Message : 6 23 4
Message with checksum : 6 23 4 33
Message after transmission : 8 20 5 33
为了加强校验和,我们可把校验和从8位改为16位(也就是进行模65536相加,取代模256),这显然把检测失败的机会从1/256减小到1/65536。尽管这是一个好的想法,但它还是会失败,因为这个公式不能满足充分的随机;通过一个简单的求和公式,每个输入的字节仅能影响校验和的一个字节,无论校验和有多宽。例如,在上面的例子中,校验和可以是MB的宽度,但依然检测不出来错误。这个问题只能通过用复杂的公司代替简单的求和公式来解决,这个复杂的公式会使每一个字节的引入都对整个校验和有影响。
因此,我们知道一个强健的校验和函数至少要要求两个方面:
宽度:校验和的宽度够宽可以提供很低的失败概率(如32位只有1/2^32的机会会失败)
复杂:这个公式使得每个输入的字节都有能力改变任意位数的校验和
3、 CRC算法背后的基本思想
当加法不能很好的形成一个有效的校验和时,除法被证明是可以的,只要除数和校验和的宽度一样就行了。
CRC算法的基本思想是把信息简单的看成一堆二进制码,用它除以别人一个固定的二进制码,把这个除法运算中的余数当成校验和。在收到信息后,接收者执行同样的除法,并把余数与校验和进行比较(校验和附加在信息的结尾)。
例如:假如信息由两个字节(6,23)组成。它们可以被当成16进制数0617,也可以被当成2进制数0000-0110-0001-0111。假如我们使用一个字节宽度的校验和。除数是常量1001,那么校验和就是0000-0110-0001-0111除以1001的余数。
...0000010101101 = 00AD = 173 = 商
____-___-___-___-
9= 1001 ) 0000011000010111 = 0617 = 1559 = 被除数
除数 0000.,,....,.,,,
----.,,....,.,,,
0000,,....,.,,,
0000,,....,.,,,
----,,....,.,,,
0001,....,.,,,
0000,....,.,,,
----,....,.,,,
0011....,.,,,
0000....,.,,,
----....,.,,,
0110...,.,,,
0000...,.,,,
----...,.,,,
1100..,.,,,
1001..,.,,,
====..,.,,,
0110.,.,,,
0000.,.,,,
----.,.,,,
1100,.,,,
1001,.,,,
====,.,,,
0111.,,,
0000.,,,
----.,,,
1110,,,
1001,,,
====,,,
1011,,
1001,,
====,,
0101,
0000,
----
1011
1001
====
0010 = 02 = 2 = 余数
尽管输入信息的每位对商的影响不怎么重要,而4位的余数却在计算后反应很大,并且如果更多字节被加入到信息(被除数)中,余数的值会从根本上很快改变。这就是除法能做到而加法做不到的。
使用这个4位校验和后,发送的信息看起来会是这样(16进制):06172(0617是信息,2是校验和)。接收者会用9除0617并且看余数是否是2。
4、 多项式运算
当涉及到CRC校验时,你会一直听到一个词:多项式。一个给定的CRC运算会被认为是使用了一个特定式的多项式,而通常的CRC算法被认为是用多项式运算进行操作。
上面被认为是正整数的除数、被除数、商和余数都被看作是带有二进制系数的多项式。这个通过把每个数看成一个位串来实现,这个数的位就是多项式的系统。例如,十进制的23是二进制的10111,所以它对应多项式
1*x^4 + 0*x^3 + 1*x^2 + 1*x^1 + 1*x^0
或者更简单点
x^4 + x^2 + x^1 + x^0
使用这种技术,信息和除数可以用多项式表达。我们可以像前面那样来做我们的运算。例如,我们想用1101乘1011。我们可以简单的把这两个多项式相乘
(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)
= (x^6 + x^4 + x^3
+ x^5 + x^3 + x^2
+ x^3 + x^1 + x^0) = x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0
这里,为了获得正确的答案,我们必须假设x=2。并间接的通过3*x^3传递一个二进制进位:
x^7 + x^3 + x^2 + x^1 + x^0
如果我们不知道x的值,我们不能执行进位。我们不知道3*x^3和x^4+x^3相等,因为我们不知道x=2。在这个真正的多页式运算中所有系数间的关系是未知的。
由于每个幂的系数都是孤立的,数学家们通过简单的改变系数操作的规则提出了各种各样不同类型的多项式运算。在这些方案中,有一个与这个运算是相关的:各系数进行模2运算,并且没有进位;所有系数必须是0或1,没有进位被算出。这个就做“模2多项式运算”。回到前面的例子:
(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)
= (x^6 + x^4 + x^3
+ x^5 + x^3 + x^2
+ x^3 + x^1 + x^0)
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0
在“模2多项式运算”中,我们不知道x的值,没有进位,所有的系数必须进行模2运算。因此,结果变成:
= x^6 + x^5 + x^4 + x^3 + x^2 + x^1 + x^0