RS-FEC的基础-有限域
目录
有限域可能是RS-FEC中最难懂的部分了,其实就数学要求而言,高中知识也许就足矣,但是现有的很多介绍RS-FEC的文章对这块知识的介绍比较简略,很难有比较系统的理解。为了学习有限域,是有必要找本数学书来看看,比如冯克勤的<<走向数学从书17 - 有限域>>,看完第一章有限域就可以了,45页纸,虽然不比看小说那么轻松,但应该不存在哪里有什么看不懂的地方,因为确实只需要高中的数学知识。这篇文章的目的不是去重复书本的内容,而是综述性的介绍一些来龙去脉和知识点的关联,类似一个读书笔记,另外还会结合”A Tutorial on Reed-Solomon Coding for Fault-Tolerance in RAID-like Systems”这篇论文来介绍一下具体的实现。
有限域从名称来来看,一个是有限的元素,另外一个是域,域可以理解为在元素构成的集合上可以做加减乘除的运算,并且满足结合律,交换律和分配律,还有满足0元素和1元素的相关特性(具体定义见书14页)。FEC如果是在无限域上运算,运算可能会溢出,精度也会有损失,有限域上因为元素有限,数值的长度可以固定,不存在溢出的情况,而且不存在会有浮点运算之类的精度问题,另外还有个好处在后面具体的知识介绍和剧透实现可以看到,有限域上的运算性能相对无限域在理论上(乘法除法可以转换为加减法,本质上类似在实数域我们也有指数和对数运算,但这个表相比元素无限个数的实数域会小很多)和实现上都可以有很大的优化(因为个数有限,可以预先计算好,运算直接查表来的到结果)。
P元有限域
对于每个素数p,可以构作出一个p元有限域Fp
p元有限域的构建
为什么要是素数? 证明之前先举个直观的例子。取模操作大家都比较熟悉了,对于m为>=2的正整数,a是整数,定义如下的一个集合,集合里面的元素[a]表示模m与a同余的所有整数构成的集合(集合里面的元素也是集合), [a]叫做模m的一个同余类。那这样就一共存在m个同余类: [0], [1], … [m-1]。
然后定义如下操作(对,是定义,不要去纠结操作的具体的现实意义,很多理论是到了几百年后才有应用场景的,有限域理论在三百多年前就出现了,到了信息时代才得到应用。不是说没有,而是说重点不在那。其实这些定义是可以根据上面的同余类的定义推导出来的):
1 | [a] + [b] = [a+b] |
可以证明基于上面的定义,加法和乘法满足交换律,结合律,分配律,而且[0]和[1]的作用和我们熟悉的有限域下一样。
其次对于加法和减法,两者互为逆运算
1 | [a] + [b] = [c] <=> [c] - [a] |
剩下最后一点有待确认: 是否存在乘法的逆运算,即除法运算。上面没有类似加减乘法来定义[a]/[b] = [a/b]
,因为首先a/b这个运算对于整数而言就没有意义,因为结果不一定是整数。根据同余类概念的定义[x]=[a]/[b] ([b]!=[0])
相当于求解同余方程b.x=a(mod)m
。对于乘法的逆运算,应该是对每个同余类[b],存在同余类[c],是的[b].[c] = [1]
1 | [a]/[b] = [a].[1]/[b] = [a].[b].[c]/[b] = [a].[c] |
例如对于m=4,对于[2],就找不到对应的[a]使得[a].[2]=[1]。
1 | [0].[2] = [0] |
具体的证明见书中第7页引理1.1.6:设m>=2, [a]为模m的同余类,则存在同余类[b]使得[a].[b] = [1]的充要条件是a,m互素。并且当a,m互素时,只有一个同余类[b]使得[a].[b]=[1]。
由引理知,对于非素数的m,总可以找到一个正因子d>1(d和m不是互素关系),也就是说[d]的逆元素不存在。
所以上面也就证明了最开始的结论: 对于每个素数p,可以构作出一个p元有限域Fp
对于p=5,可以有如下的F5的乘法表:
1 | [0] [1] [2] [3] [4] |
p元有限域的特性
后面为了书写方便,有限域内的数会省略掉”[]”。下面是一些p元有限域的一些重要的特性:
Fp中的每个元素a,均有a^^p = a,如果a!=0,则a^^(p-1) = 1
例如: 3^^4 = 3.3.3.3 = 4.3.3 = 2.3 = 1
证明: 因为共有p个不同的元素,所以当a!=0时,a, 2a, 3a, (p-1)a也分别是p个不同的元素, 所以1.2 … p-1 = a. 2a. 3a. (p-1)a,消去1.2 … p-1,得到a^^(p-1) = 1。
Fp中非0元素a的阶定义为最小的正整数n,有a^^n = 1。因为上面已经证明了a^^(p-1)=1,所以n一定是存在的。a的阶n一定是p-1的因子。
另外,如果Fp中存在元素g,且g的阶是p-1,那么1=g^^0, g^^1, g^^(p-2)分别对于p-1个不同的元素。(见11页“注纪”里的证明),也就是说非零元素都可以由g来生成,这个就称为本原元素,有了这个,Fp的数学结构就简单了。
对于上面p=5的例子, 3就是一个本原元素:
1 | 3^^0 = 1 |
有了本原元素指数方式来表示每一个元素,乘法运算和除法运算就可以转换为加法和减法运算了。
问题到这里并没有结束,虽然我们知道对于素数p存在p元有限域,但给定任意一个素数,除了穷举法,我们没办法快速找出其本原元素。如果大家看过RS-FEC相关的文献,就会发现里面有用到“生成多项式”,后面将介绍怎么会和多项式有关系的。
任意有限域
元素个数
有限域F的元素个数必为p^^n,其中p为素数, n>=1。由前面可知,Fp是F的一个子域,利用最小生成集,可以将F中的元素表示为x = a1.x1 + ... + an.xn
,可以证明对于任意的x,ai系数是唯一的。ai的组合共有p^^n个,所以共有p^^n个元素。
存在性
对于每个素数幂p^^n,均存在p^^n元有限域(多项式x^^(p^^n)-x
的在Fp的代数闭包中的所有根形成p^^n元有限域)。这里的证明用到了重根判别法,然后证明根构成一个域。
唯一性
对于Fp的代数闭包F,其中恰好由一个p^^n元有限域。
令q=p^^n,F中非零元素为x[1], …, x[q-1](这里借用数组的[]来表示下标),每个元素乘以非零元素a,由x[1]. … . x[q-1] = a^^(q-1).x[1]. … .x[q-1]。则由a^^(q-1)=1,即a^^q=a。从而每个元素都是f(x) = x^^q-x = x^^(p^^n)-x
的根,且只有p^^n个根,从而证明了唯一性。
本原元素的存在性
q=p^^n,q-1阶元素叫做q元域Fq的本原元素。有了本原元素后,做乘法操作就很方便了。但加法应该怎么做呢?
构建有限域
Fp中的n次不可约首一多项式f(x),也是RS-FEC文献中的生成多项式。设a为f(x)的一个根,那么集合
1 | S = {c[0].a^^(n-1) + c[1].a^^(n-2) + ... + c[n-1] |
是一个q元域,q=p^^n。
这里的证明思路是先证明S中有p^^n个不同的元素,其次再根据域的定义来证明S中的元素上的操作满足定义。
有了这个结论后,加法操作就是多项式的加法操作(系数属于Fp)。
例子
F3[x]中的不可约多项式f(x)=x^^2 + x + 2
(如果可约,那一定会有一次多项式因子,使得其在F3中有根,但f(0)=2, f(1)=1, f(2)=2,都不为0,所以f(x)不可约)。根据上面的定理,设a为f(x)在扩域的一个根,则有a^^2 + a + 2 = 0 <=> a^^2 = 2a + 1
,那么对于n=2, p=3, q=p^^n=3^^2=9元有限域F9有如下9个元素:
1 | S = {c[0].a + c[1]} = {0, 1, 2, a, a+1, a+2, 2a, 2a+1, 2a+2} |
此外可以看到a是8阶元素,即是F9的本原元素:
1 | a^^2 = 2a + 1 |
所以F9中的元素有如下两种表示方式,纪c[0].a + c[1]为(c[0], c[1]):
1 | 0 = (0, 0) 1 = (0, 1) a = (1, 0) |
在计算乘法的时候可以用左边的表达方式操作:
1 | (2a + 1).(a + 2) = (2, 1).(1, 2) = a^^2 . a^^6 = a^^8 = 1 = (0, 1) |
加法可以用右边的表达方式来操作:
1 | a^^3 + a^^5 = (2, 2) + (2, 0) = (1, 2) = a^^6 |
代码实现
实现中都会取p=2, 对应的n次不可约多项式分别为:
1 | n = 2: x^^2 + x + 1 |
下面以n=4为例,GF(p^^n) = GF(2^^4) = 16,即16元有限域,设a是x^^4 + x + 1
的一个解,即有a^^4 = a + 1
,下面可用看的a也为本原元素(a^^15=1,只有本原元素才能生成所有的非零元素,也才能用于指数方式的表达),16元有限域的元素就仅仅限于前16项,a^^15和a^^0是同一个元素。
指数方式 | 多项式方式 | 二进制 |
---|---|---|
0 | 0 | 0000 |
a^^0 | 1 | 0001 |
a^^1 | a | 0010 |
a^^2 | a^^2 | 0100 |
a^^3 | a^^3 | 1000 |
a^^4 | a + 1 | 0011 |
a^^5 | a^^2 + a | 0110 |
a^^6 | a^^3 + a^^2 | 1100 |
a^^7 | a^^3 + a + 1 | 1011 |
a^^8 | a^^2 + 1 | 0101 |
a^^9 | a^^3 + a | 1010 |
a^^10 | a^^2 + a +1 | 0111 |
a^^11 | a^^3 + a^^2 + a | 1110 |
a^^12 | a^^3 + a^^2 + a + 1 | 1111 |
a^^13 | a^^3 + a^^2 + 1 | 1101 |
a^^14 | a^^3 + 1 | 1001 |
a^^15 | 1 | 0001 |
上面可以很清楚的看到二进制方式只是多项式方式略去a后的一种缩写的表示方式而已。而指数方式则是根据a^^4 = a + 1
带入运算的到的:
1 | a^^4 = a + 1 |
如果要讲上面的计算过程转换为代码:
a^^7实际上可以a^^6 . a,也就是a^^6的二进制方式左移一位,从1100(a^^3 + a^^2
)变成11000(a^^4 + a^^3
),因为a^^4 + a + 1 = 0
,任何元素加上0不变,所以a^^4 + a^^3 + a^^4 + a + 1
,转换为XOR操作(11000 ^ 10011 = 01011),即a^^3 + a + 1
。
可以看到加法操作其实也就是系数为F2的多项式加法,也就是系数的XOR运算,乘法可以先转换为指数方式,然后做(模2^^n-1=15)加法,然后转换为多项式方式。
上面的过程转换为c语言的实现,首先是要生成两个表:
- gflog用来将“多项式方式 => 指数方式”(根据二进制方式,范围从1到2^^n-1,查找对应的指数),
- gfilog用来将“指数方式 => 多项式方式”(根据指数方式,范围从0到2^^n-2,查找对应的二进制方式)。
1 | unsigned int prim_poly_4 = 023; //x^^4 + x + 1 => 0b10011 = 023 |
然后是乘法和除法操作,加法减法对于p=2的情况等同于XOR操作。
1 | #define NW (1 << w) /* In other words, NW equals 2 to the w-th power */ int mult(int a, int b) { int sum_log; if (a == 0 || b == 0) return 0; sum_log = gflog[a] + gflog[b]; if (sum_log >= NW-1) sum_log -= NW-1; return gfilog[sum_log]; } |