【Applied Algebra】扩域(Galois域)上的乘法表构造
在之前的文章里,我们讨论了扩域上(Galois域)的计算及其实现,但是侧重的是扩域中元素之间运算的细节实现,而如果想描述整个域的结构,就需要构造乘法表和加法表;实现仍然是基于c++和符号计算库GiNaC;
运算表及其设计
考虑 F p n \mathbb{F}_{p^n} Fpn上的元素,根据 F p n ≅ F p / f ( x ) \mathbb{F}_{p^n} \cong \mathbb{F}_{p}/f(x) Fpn≅Fp/f(x),比如计算 F 2 4 \mathbb{F}_{2^4} F24上的元素,根据 F 2 4 ≅ F 2 / f ( x ) \mathbb{F}_{2^4} \cong \mathbb{F}_{2}/f(x) F24≅F2/f(x),这里设 f ( x ) = x 4 + x 3 + 1 f(x) = x^4+x^3+1 f(x)=x4+x3+1为不可约多项式; F 2 4 \mathbb{F}_{2^4} F24中洽有 16 16 16个元素, F 2 / f ( x ) \mathbb{F}_{2}/f(x) F2/f(x)中的元素可以表示为小于 4 4 4次的多项式,根据每一项出现与否( { x 3 , x 2 , x , 1 } \{x^3,x^2,x,1\} {x3,x2,x,1})也恰有16个多项式,那么和 F 2 4 \mathbb{F}_{2^4} F24中的元素一一对应,因此 F 2 4 \mathbb{F}_{2^4} F24亦可表示为:
F 2 4 = { 0000 , 0001 , . . . , 1111 } \mathbb{F}_{2^4} = \{0000,0001,...,1111\} F24={0000,0001,...,1111}
更一般地,我们设 F p n \mathbb{F}_{p^n} Fpn形如 F p n = { g 1 , g 2 , ⋯ , g N } \mathbb{F}_{p^n} = \{g_1,g_2,\cdots,g_N\} Fpn={g1,g2,⋯,gN},其中 N N N是域的阶.那么 F p n \mathbb{F}_{p^n} Fpn中任何元素 g i g_i gi都可看作 n n n位 p p p进制数字串,它的每一位若一一对应地看作 { x n − 1 , . . . , x 2 , x , 1 } \{x^{n-1},...,x^2,x,1\} {xn−1,...,x2,x,1}的系数,那么就和多项式环 F p / f ( x ) \mathbb{F}_{p}/f(x) Fp/f(x)中的元素一一对应了.
综上所述,乘法表或者加法表的构造就是,利用扩域 F p n \mathbb{F}_{p^n} Fpn和多项式环 F p / f ( x ) \mathbb{F}_{p}/f(x) Fp/f(x)中元素的一一对应,通过多项式环 F p / f ( x ) \mathbb{F}_{p}/f(x) Fp/f(x)中的元素的运算结果来对应还原扩域 F p n \mathbb{F}_{p^n} Fpn的结构,我们首先设计一个类来抽象扩域 F p n \mathbb{F}_{p^n} Fpn:
class extension_field_class
{
public:
extension_field* pt_EXT_FIELD;
GiNaC::ex IRRED_POLY;
int ORDER_NUM;
std::vector<extension_field_element> ELEMENTS_SET;
GiNaC::exmap TAB_POLY2INDEX;
std::map<extension_field_element, int> TAB_ELEMENT2INDEX;
std::vector<std::vector<int> > TAB_MULTI;
std::vector<std::vector<int> > TAB_DIV;
std::vector<std::vector<int> > TAB_ADD;
std::vector<std::vector<int> > TAB_SUB;
extension_field_class(extension_field* pt_EXT_FIELD,const GiNaC::ex& IRRED_POLY);
extension_field_element multi_computing(const extension_field_element& ELEMENT_1,const extension_field_element& ELEMENT_2);
extension_field_element add_computing(const extension_field_element& ELEMENT_1,const extension_field_element& ELEMENT_2);
extension_field_element div_computing(const extension_field_element& ELEMENT_1,const extension_field_element& ELEMENT_2);
extension_field_element sub_computing(const extension_field_element& ELEMENT_1,const extension_field_element& ELEMENT_2);
void print_multi_tab();
};
其中 TAB_MULTI 和 TAB_ADD 就通过域元素的序号存储了对应两个元素的乘法和加法运算结果.
运算表及其实现
我们在上一节里构造了扩域元素的类 extension_field_element,因此实现运算表构造的功能就是基于我们之前弄的乘法和加法运算符,但在明确任意两个元素相乘的结果之前,我们首先要构造所有的域元素,这是之前没有实现的功能:
// --------- Constructing ELEMENTS_SET; ---------
std::vector<int> COEFF_VEC;std::vector<GiNaC::ex> MONOMIALS;
for (int INDEX_j = 0; INDEX_j < this->pt_EXT_FIELD->EXTEN_NUM; ++INDEX_j){COEFF_VEC.push_back(0);}
for (int INDEX_j = 0; INDEX_j < this->pt_EXT_FIELD->EXTEN_NUM; ++INDEX_j)
{
GiNaC::ex MONOMIAL = 1;
if (INDEX_j==0){MONOMIALS.push_back(MONOMIAL);continue;}
for (int INDEX_k = 1; INDEX_k <= INDEX_j; ++INDEX_k){MONOMIAL*=a;}
MONOMIALS.push_back(MONOMIAL);
}
for (int INDEX_i = 1; INDEX_i < this->ORDER_NUM; ++INDEX_i)
{
GiNaC::ex POLYNOMIAL = 0;
func_decimal_conv(INDEX_i,this->pt_EXT_FIELD->PRIME_NUM,this->pt_EXT_FIELD->EXTEN_NUM,COEFF_VEC);
for (int INDEX_j = 0; INDEX_j < this->pt_EXT_FIELD->EXTEN_NUM; ++INDEX_j)
{
POLYNOMIAL += COEFF_VEC[INDEX_j]*MONOMIALS[INDEX_j];
}
extension_field_element EXT_FIELD_ELE(this->pt_EXT_FIELD,POLYNOMIAL);
EXT_FIELD_ELE.ELEMENT_INDEX = INDEX_i-1;
this->ELEMENTS_SET.push_back(EXT_FIELD_ELE);
this->TAB_POLY2INDEX[POLYNOMIAL] = EXT_FIELD_ELE.ELEMENT_INDEX;
this->TAB_ELEMENT2INDEX[EXT_FIELD_ELE] = EXT_FIELD_ELE.ELEMENT_INDEX;
}
其中 TAB_POLY2INDEX 和 TAB_ELEMENT2INDEX 可以帮助我们以线性复杂度 O ( 1 ) O(1) O(1)根据多项式或域元素类定位该元素的下标,这有利于后续算法的效率,注意 TAB_ELEMENT2INDEX 是map类型,当extension_field_element作为key的时候,需要实现它的"大小比较功能"(这是由于map是基于红黑树实现的),在此不加赘述.构造完所有元素之后,即可来构造乘法表和加法表:
// --------- Constructing TAB_MULTI and TAB_DIV; ---------
std::vector<std::vector<int> > TAB_NEW = init_all_tab(this->ORDER_NUM-1);
this->TAB_MULTI = TAB_NEW;this->TAB_ADD = TAB_NEW;this->TAB_DIV = TAB_NEW;this->TAB_SUB = TAB_NEW;
for (int INDEX_i = 0; INDEX_i < this->ORDER_NUM-1; ++INDEX_i)
{
for (int INDEX_j = 0; INDEX_j <= INDEX_i; ++INDEX_j)
{
GiNaC::ex POLYNOMIAL = (this->ELEMENTS_SET[INDEX_i]*this->ELEMENTS_SET[INDEX_j]).POLYNOMIAL;
this->TAB_MULTI[INDEX_i][INDEX_j] = GiNaC::ex_to<GiNaC::numeric>(this->TAB_POLY2INDEX[POLYNOMIAL]).to_int();
this->TAB_MULTI[INDEX_j][INDEX_i] = GiNaC::ex_to<GiNaC::numeric>(this->TAB_POLY2INDEX[POLYNOMIAL]).to_int();
this->TAB_DIV[ TAB_MULTI[INDEX_j][INDEX_i] ][INDEX_i] = INDEX_j;
this->TAB_DIV[ TAB_MULTI[INDEX_j][INDEX_i] ][INDEX_j] = INDEX_i;
}
}
// --------- Constructing TAB_ADD; ---------
for (int INDEX_i = 0; INDEX_i < this->ORDER_NUM-1; ++INDEX_i)
{
for (int INDEX_j = 0; INDEX_j <= INDEX_i; ++INDEX_j)
{
GiNaC::ex POLYNOMIAL = (this->ELEMENTS_SET[INDEX_i] + this->ELEMENTS_SET[INDEX_j]).POLYNOMIAL;
this->TAB_ADD[INDEX_i][INDEX_j] = GiNaC::ex_to<GiNaC::numeric>(this->TAB_POLY2INDEX[POLYNOMIAL]).to_int();
this->TAB_ADD[INDEX_j][INDEX_i] = GiNaC::ex_to<GiNaC::numeric>(this->TAB_POLY2INDEX[POLYNOMIAL]).to_int();
}
}
这里顺带构造了除法表,减法表有点特殊,思考一下为什么(提示:涉及到特殊元素 0 0 0).
通过这样的功能实现,我们可以打印任意扩域 F p n \mathbb{F}_{p^n} Fpn的运算表,在这里我们尝试构造扩域 F 3 3 \mathbb{F}_{3^3} F33的乘法表:
GiNaC::ex IRRED_POLY = a*a*a + 2*a*a + 1;
struct extension_field EXT_FIELD;
EXT_FIELD.PRIME_NUM = 3;EXT_FIELD.EXTEN_NUM = 3;EXT_FIELD.IRRED_POLY = IRRED_POLY;
extension_field_class EXT_FIELD_CLASS(&EXT_FIELD,IRRED_POLY);
EXT_FIELD_CLASS.print_multi_tab();
结果如下:
====================== Multiplication Table of GF(3^3) ======================
* | g_0 |g_1 |g_2 |g_3 |g_4 |g_5 |g_6 |g_7 |g_8 |g_9 |g_10 |g_11 |g_12 |g_13 |g_14 |g_15 |g_16 |g_17 |g_18 |g_19 |g_20 |g_21 |g_22 |g_23 |g_24 |g_25 |
g_0 | g_0 g_1 g_2 g_3 g_4 g_5 g_6 g_7 g_8 g_9 g_10 g_11 g_12 g_13 g_14 g_15 g_16 g_17 g_18 g_19 g_20 g_21 g_22 g_23 g_24 g_25
g_1 | g_1 g_0 g_5 g_7 g_6 g_2 g_4 g_3 g_17 g_19 g_18 g_23 g_25 g_24 g_20 g_22 g_21 g_8 g_10 g_9 g_14 g_16 g_15 g_11 g_13 g_12
g_2 | g_2 g_5 g_8 g_11 g_14 g_17 g_20 g_23 g_10 g_13 g_16 g_19 g_22 g_25 g_1 g_4 g_7 g_18 g_21 g_24 g_0 g_3 g_6 g_9 g_12 g_15
g_3 | g_3 g_7 g_11 g_15 g_10 g_23 g_18 g_22 g_19 g_20 g_24 g_4 g_5 g_0 g_16 g_8 g_12 g_9 g_13 g_14 g_21 g_25 g_17 g_6 g_1 g_2
g_4 | g_4 g_6 g_14 g_10 g_12 g_20 g_25 g_18 g_1 g_3 g_5 g_16 g_9 g_11 g_22 g_24 g_17 g_0 g_2 g_7 g_15 g_8 g_13 g_21 g_23 g_19
g_5 | g_5 g_2 g_17 g_23 g_20 g_8 g_14 g_11 g_18 g_24 g_21 g_9 g_15 g_12 g_0 g_6 g_3 g_10 g_16 g_13 g_1 g_7 g_4 g_19 g_25 g_22
g_6 | g_6 g_4 g_20 g_18 g_25 g_14 g_12 g_10 g_0 g_7 g_2 g_21 g_19 g_23 g_15 g_13 g_8 g_1 g_5 g_3 g_22 g_17 g_24 g_16 g_11 g_9
g_7 | g_7 g_3 g_23 g_22 g_18 g_11 g_10 g_15 g_9 g_14 g_13 g_6 g_2 g_1 g_21 g_17 g_25 g_19 g_24 g_20 g_16 g_12 g_8 g_4 g_0 g_5
g_8 | g_8 g_17 g_10 g_19 g_1 g_18 g_0 g_9 g_16 g_25 g_7 g_24 g_6 g_15 g_5 g_14 g_23 g_21 g_3 g_12 g_2 g_11 g_20 g_13 g_22 g_4
g_9 | g_9 g_19 g_13 g_20 g_3 g_24 g_7 g_14 g_25 g_5 g_15 g_0 g_10 g_17 g_11 g_21 g_4 g_12 g_22 g_2 g_23 g_6 g_16 g_1 g_8 g_18
g_10 | g_10 g_18 g_16 g_24 g_5 g_21 g_2 g_13 g_7 g_15 g_23 g_12 g_20 g_4 g_17 g_1 g_9 g_3 g_11 g_22 g_8 g_19 g_0 g_25 g_6 g_14
g_11 | g_11 g_23 g_19 g_4 g_16 g_9 g_21 g_6 g_24 g_0 g_12 g_14 g_17 g_2 g_7 g_10 g_22 g_13 g_25 g_1 g_3 g_15 g_18 g_20 g_5 g_8
g_12 | g_12 g_25 g_22 g_5 g_9 g_15 g_19 g_2 g_6 g_10 g_20 g_17 g_3 g_16 g_13 g_23 g_0 g_4 g_14 g_18 g_24 g_1 g_11 g_8 g_21 g_7
g_13 | g_13 g_24 g_25 g_0 g_11 g_12 g_23 g_1 g_15 g_17 g_4 g_2 g_16 g_18 g_19 g_3 g_14 g_22 g_6 g_8 g_9 g_20 g_7 g_5 g_10 g_21
g_14 | g_14 g_20 g_1 g_16 g_22 g_0 g_15 g_21 g_5 g_11 g_17 g_7 g_13 g_19 g_6 g_12 g_18 g_2 g_8 g_23 g_4 g_10 g_25 g_3 g_9 g_24
g_15 | g_15 g_22 g_4 g_8 g_24 g_6 g_13 g_17 g_14 g_21 g_1 g_10 g_23 g_3 g_12 g_19 g_5 g_20 g_0 g_16 g_25 g_2 g_9 g_18 g_7 g_11
g_16 | g_16 g_21 g_7 g_12 g_17 g_3 g_8 g_25 g_23 g_4 g_9 g_22 g_0 g_14 g_18 g_5 g_13 g_11 g_19 g_6 g_10 g_24 g_2 g_15 g_20 g_1
g_17 | g_17 g_8 g_18 g_9 g_0 g_10 g_1 g_19 g_21 g_12 g_3 g_13 g_4 g_22 g_2 g_20 g_11 g_16 g_7 g_25 g_5 g_23 g_14 g_24 g_15 g_6
g_18 | g_18 g_10 g_21 g_13 g_2 g_16 g_5 g_24 g_3 g_22 g_11 g_25 g_14 g_6 g_8 g_0 g_19 g_7 g_23 g_15 g_17 g_9 g_1 g_12 g_4 g_20
g_19 | g_19 g_9 g_24 g_14 g_7 g_13 g_3 g_20 g_12 g_2 g_22 g_1 g_18 g_8 g_23 g_16 g_6 g_25 g_15 g_5 g_11 g_4 g_21 g_0 g_17 g_10
g_20 | g_20 g_14 g_0 g_21 g_15 g_1 g_22 g_16 g_2 g_23 g_8 g_3 g_24 g_9 g_4 g_25 g_10 g_5 g_17 g_11 g_6 g_18 g_12 g_7 g_19 g_13
g_21 | g_21 g_16 g_3 g_25 g_8 g_7 g_17 g_12 g_11 g_6 g_19 g_15 g_1 g_20 g_10 g_2 g_24 g_23 g_9 g_4 g_18 g_13 g_5 g_22 g_14 g_0
g_22 | g_22 g_15 g_6 g_17 g_13 g_4 g_24 g_8 g_20 g_16 g_0 g_18 g_11 g_7 g_25 g_9 g_2 g_14 g_1 g_21 g_12 g_5 g_19 g_10 g_3 g_23
g_23 | g_23 g_11 g_9 g_6 g_21 g_19 g_16 g_4 g_13 g_1 g_25 g_20 g_8 g_5 g_3 g_18 g_15 g_24 g_12 g_0 g_7 g_22 g_10 g_14 g_2 g_17
g_24 | g_24 g_13 g_12 g_1 g_23 g_25 g_11 g_0 g_22 g_8 g_6 g_5 g_21 g_10 g_9 g_7 g_20 g_15 g_4 g_17 g_19 g_14 g_3 g_2 g_18 g_16
g_25 | g_25 g_12 g_15 g_2 g_19 g_22 g_9 g_5 g_4 g_18 g_14 g_8 g_7 g_21 g_24 g_11 g_1 g_6 g_20 g_10 g_13 g_0 g_23 g_17 g_16 g_3
一个额外的思考(😉):仍然考虑多项式环 F p n [ x 1 , ⋯ , x m ] \mathbb{F}_{p^n}[x_1,\cdots,x_m] Fpn[x1,⋯,xm]上的方程组,由于我们已经计算得到了 F p n \mathbb{F}_{p^n} Fpn上的加法乘法表 T × \mathcal{T}_{\times} T×,加法表 T + \mathcal{T}_{+} T+,除法表 T / \mathcal{T}_{/} T/,则我们可以查表得到 T ( g i , g j , + ) = g k \mathcal{T}(g_i,g_j,+)=g_k T(gi,gj,+)=gk和 T ( g i , g j , × ) = g l \mathcal{T}(g_i,g_j,\times)=g_l T(gi,gj,×)=gl乃至 T ( g i , g j , / ) = g h \mathcal{T}(g_i,g_j,/)=g_h T(gi,gj,/)=gh来获得符号运算结果,那多项式环 F p n [ x 1 , ⋯ , x m ] \mathbb{F}_{p^n}[x_1,\cdots,x_m] Fpn[x1,⋯,xm]的多项式 g ( x ) g(x) g(x)形如:
g ( x ) ∈ { ∑ i g i M i ∣ g i ∈ F p n , M i = x 1 α i . . . x n α n } g(x) \in \{\sum_i g_{i}M_i |g_i \in \mathbb{F}_{p^n},M_i = x^{\alpha_i}_1...x^{\alpha_n}_n\} g(x)∈{i∑giMi∣gi∈Fpn,Mi=x1αi...xnαn}
请你思考一下如何实现这种方程组的高斯消元算法(回忆线性代数里的做法,对齐项 M i M_i Mi即可…).
参考资料
[1] Modern abstract algebra, Anderson, M. and Feil, T.,2014.
[2] 应用近世代数(第三版),胡冠章,清华大学出版社.