上上次 说了一个模n矩阵的快速计算的两种方法,其实如果能把矩阵“对角化”的话,还可以进一步减少运算复杂度(不过这种方法在模n/模p下比较看运气);另外,上次 在说GL(n,p)的群阶时说了两个简略的证明,如果用Jordan Canonical Form来解释的话好像会更清晰.
普通矩阵的对角化·
这里说的“普通矩阵”,其实是指线性代数里学的R \mathbb{R} R 上的矩阵,它的对角化方法在[GSLA]的6.2节有说到;给定一个n ∗ n n*n n ∗ n 的非奇异矩阵A A A ,要求它的对角矩阵Λ \Lambda Λ 和对角化变化矩阵X X X 的话:
先求出A A A 的特征多项式 p A ( x ) p_A(x) p A ( x ) ,根据Cayley–Hamilton定理 :
p A ( λ ) = d e t ( A − λ I n ∗ n ) p_A(\lambda) = det(A-\lambda I_{n*n})
p A ( λ ) = d e t ( A − λ I n ∗ n )
通过解p A ( λ ) = 0 p_A(\lambda)=0 p A ( λ ) = 0 可以解出A A A 的n n n 个特征值λ 1 , . . . , λ n \lambda_1,...,\lambda_n λ 1 , ... , λ n ,如果这n n n 个特征值各不相同的话,就直接有:
Λ = ( λ 1 0 ⋯ 0 0 λ 2 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ λ n ) \Lambda =
\begin{pmatrix}
\lambda_1 & 0 & \cdots & 0 \\
0 & \lambda_2 & \cdots & 0 \\
\vdots & \vdots & \ddots & \vdots \\
0 & 0 & \cdots & \lambda_n \\
\end{pmatrix}
Λ = ⎝ ⎛ λ 1 0 ⋮ 0 0 λ 2 ⋮ 0 ⋯ ⋯ ⋱ ⋯ 0 0 ⋮ λ n ⎠ ⎞
接着解出n n n 个特征值对应的n n n 个特征向量x 1 , . . . , x n x_1,...,x_n x 1 , ... , x n ,通过解方程:
( A − λ i I ) x i = 0 (A-\lambda_iI)x_i = 0
( A − λ i I ) x i = 0
可以解出各x i x_i x i ,然后就有:
X = [ x 1 , x 2 , . . . , x n ] X = [x_1, x_2, ..., x_n]
X = [ x 1 , x 2 , ... , x n ]
可以简单验算一下有:
A X = X Λ = [ λ 1 x 1 , λ 2 x 2 , . . . , λ n x n ] AX = X\Lambda =
[\lambda_1x_1, \lambda_2x_2, ..., \lambda_nx_n]
A X = X Λ = [ λ 1 x 1 , λ 2 x 2 , ... , λ n x n ]
最后对角化其实就是:
A = X Λ X − 1 A = X \Lambda X^{-1}
A = X Λ X − 1
矩阵对角化的经典应用就是用来加快求幂:
A k = X Λ k X − 1 A^k = X \Lambda^k X^{-1}
A k = X Λ k X − 1
简要证明一下:原本A k A^k A k 的话需要做k k k 次(优化后l o g ( k ) log(k) l o g ( k ) 次)n ∗ n n*n n ∗ n 的矩阵乘法,每次复杂度是O ( n 3 ) O(n^3) O ( n 3 ) ,(优化后是O ( n 2 + α ) O(n^{2+\alpha}) O ( n 2 + α ) );对角化后的Λ k \Lambda^k Λ k 需要做k k k 次(优化后l o g ( k ) log(k) l o g ( k ) 次)n ∗ n n*n n ∗ n 对角矩阵 的乘法,每次复杂度O ( n ) O(n) O ( n ) ,另外加两次矩阵乘法;所以在k k k 很大的情况下是可以大大提高运算速度的:
O ( l o g ( k ) ) ∗ O ( n ) + 2 ∗ O ( n 2 + α ) ≈ O ( n ∗ l o g ( k ) ) O(log(k))*O(n)+2*O(n^{2+\alpha}) \approx O(n*log(k))
O ( l o g ( k )) ∗ O ( n ) + 2 ∗ O ( n 2 + α ) ≈ O ( n ∗ l o g ( k ))
但是,在普通矩阵对角化的时候还有一些问题,比如:
特征多项式p A ( x ) = 0 p_A(x)=0 p A ( x ) = 0 无实数解的时候怎么办?
解出的特征值有重解(λ i = λ j \lambda_i=\lambda_j λ i = λ j )的话怎么办?
域扩张(field extensions)·
对于第一个问题,可以用域扩张 的方法,把实数域R \mathbb{R} R 扩张到复数域C \mathbb{C} C ,即原来A A A 是在R \mathbb{R} R 上做运算的,现在改成在C \mathbb{C} C 上运算,那么p A ( x ) = 0 p_A(x)=0 p A ( x ) = 0 就总会有解,只不过有的可能是复数解(有i i i ),Λ \Lambda Λ 和X X X 上也会有复数。
对于第二个问题,可以用Jordan Form 来解决;Jordan Form(Jordan Matrix)就是k k k 个块(Jordan Block)组成的块对角矩阵,如:
( J 1 0 ⋯ 0 0 J 2 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ J k ) \begin{pmatrix}
J_1 & 0 & \cdots & 0 \\
0 & J_2 & \cdots & 0 \\
\vdots & \vdots & \ddots & \vdots \\
0 & 0 & \cdots & J_k \\
\end{pmatrix}
⎝ ⎛ J 1 0 ⋮ 0 0 J 2 ⋮ 0 ⋯ ⋯ ⋱ ⋯ 0 0 ⋮ J k ⎠ ⎞
其中,k k k 等于A A A 的所有特征值的几何重数 之和,也有说是独立的特征向量(independent eigenvectors)个数;一般情况下是p A ( λ ) p_A(\lambda) p A ( λ ) 的不同根的个数,但也有相同的根λ i \lambda_i λ i 对应多个Jordan Block的情况(,好像还需要看( A − λ i I ) (A-\lambda_iI) ( A − λ i I ) 对应零空间的维度?),可以参考下这里 的一个例子;
Jordan Matrix中,λ i \lambda_i λ i 对应的一个J i J_i J i 长这样子:
( λ i 1 0 λ i 1 ⋱ ⋱ λ i 1 0 λ i ) \begin{pmatrix}
\lambda_i & 1 & & & 0 \\
& \lambda_i & 1 & & \\
& & \ddots & \ddots \\
& & & \lambda_i & 1 \\
0 & & & & \lambda_i \\
\end{pmatrix}
⎝ ⎛ λ i 0 1 λ i 1 ⋱ ⋱ λ i 0 1 λ i ⎠ ⎞
即对角线上是λ i \lambda_i λ i ,对角线上一个(叫上对角线?)是1 1 1 ;其中每一个J i J_i J i 的大小可以通过下面方式计算:
任何的非奇异方阵A A A 都会相似于一个Jordan矩阵J J J ,即可以找到一个B B B ,使得:
A = B J B − 1 A = BJB^{-1}
A = B J B − 1
(顺便贴一下[GSLA] 8.3的说法,感觉比我说的简洁)
所以如果可以搞出J J J 的话,也可以用类似对角化的方法加快幂运算:
A k = B J k B − 1 A^k = B J^k B^{-1}
A k = B J k B − 1
其中J J J 做了k k k 次方后仍然是只有“两条对角线”的的矩阵,即也是k k k 次(优化后l o g ( k ) log(k) l o g ( k ) 次)O ( n ) O(n) O ( n ) 的矩阵乘法;
J J J 的求法可以参考知乎的一个专栏 ;B B B 的求法可以参考维基百科 ;大概就是:
求p A ( λ ) p_A(\lambda) p A ( λ ) 和各λ i \lambda_i λ i ;
求λ i \lambda_i λ i 对应的Jordan块的阶数,根据阶数构造Jordan块,然后拼接Jordan矩阵;
如果J i J_i J i 的阶数为1 1 1 的话,B B B 中对应那一列就是λ i \lambda_i λ i 对应的特征向量,即计算
( A − λ i I ) b i = 0 (A-\lambda_iI)b_i = 0
( A − λ i I ) b i = 0
如果J i J_i J i 阶数为l > 1 l>1 l > 1 的话,即可以算出B B B 中的l l l 个向量,其中第一个可以是λ i \lambda_i λ i 的特征向量:
( A − λ i I ) b i , 1 = 0 (A-\lambda_iI)b_{i, 1} = 0
( A − λ i I ) b i , 1 = 0
然后其他剩余的l − 1 l-1 l − 1 个向量可以通过解:
( A − λ i I ) b i , j = b i , j − 1 (A-\lambda_iI)b_{i, j} = b_{i, j-1}
( A − λ i I ) b i , j = b i , j − 1
得出(不过写代码时好像见到有无解的情况。。。 )
GL(n, p)上的对角化·
G L ( n , F p ) GL(n, \mathbb{F}_p) G L ( n , F p ) 上的对角化其实跟R \mathbb{R} R 上的差不多 ,只不过有一些需要注意的问题。
求特征值·
按上面的说法,给定一个矩阵A A A ,首先需要求它的特征多项式p A ( λ ) p_A(\lambda) p A ( λ ) ,求的方法是一样的,即解:
p A ( λ ) = d e t ( A − λ I ) , i n F p p_A(\lambda) = det(A-\lambda I),\ \ in\ \mathbb{F}_p
p A ( λ ) = d e t ( A − λ I ) , in F p
只不过运算是在域F p \mathbb{F}_p F p 上进行的;解特征值的方法也是一样的,即解:
p A ( λ ) = 0 , i n F p p_A(\lambda)=0, \ \ in\ \mathbb{F}_p
p A ( λ ) = 0 , in F p
然后这里就有了第一个问题:F p \mathbb{F}_p F p 上的方程怎么解 ?
首先可以对p A ( λ ) p_A(\lambda) p A ( λ ) 进行分解,比如用[Ber70 ]的方法 ,但后来发现其实用Sage就可以直接分解,只不过要把域定义好:
1 2 3 4 5 def factorFp (f, p ): F.<a> = GF(p) R.<x> = PolynomialRing(F) return R(f).factor()
如果p A ( λ ) p_A(\lambda) p A ( λ ) 可以被分解为若干个度(degree)为1 1 1 的多项式(即最高次数项是λ \lambda λ )的乘积,那么方程就可以直接被解了;比如在F 3 \mathbb{F}_3 F 3 上有:
p A ( λ ) = λ 3 + λ 2 − λ − 1 = 0 , i n F 3 p_A(\lambda)=\lambda^3+\lambda^2-\lambda-1=0, \ \ in\ \mathbb{F}_3
p A ( λ ) = λ 3 + λ 2 − λ − 1 = 0 , in F 3
可以被分解为:
( λ + 2 ) ∗ ( λ + 1 ) 2 = 0 , i n F 3 (\lambda+2) * (\lambda+1)^2 = 0, \ \ in\ \mathbb{F}_3
( λ + 2 ) ∗ ( λ + 1 ) 2 = 0 , in F 3
就可以解出:
λ = 1 , 2 , 2 ; i n F 3 \lambda = 1, 2, 2\ ; \ \ in\ \mathbb{F}_3
λ = 1 , 2 , 2 ; in F 3
如果p A ( λ ) p_A(\lambda) p A ( λ ) 不能完全被分解为degree为1 1 1 的多项式(或者直接不能被分解)的话,则需要使用域扩张的方法,扩展到更大的域上;比如在F 3 \mathbb{F}_3 F 3 上有:
p A ( λ ) = λ 3 + λ 2 − λ + 1 = 0 , i n F 3 p_A(\lambda)=\lambda^3+\lambda^2-\lambda+1=0, \ \ in\ \mathbb{F}_3
p A ( λ ) = λ 3 + λ 2 − λ + 1 = 0 , in F 3
在F 3 \mathbb{F}_3 F 3 上p A ( λ ) p_A(\lambda) p A ( λ ) 是不可约的,于是可以把F 3 \mathbb{F}_3 F 3 扩张到:
F 3 3 = F 3 [ λ ] / ( λ 3 + λ 2 − λ + 1 ) \mathbb{F}_{3^3}=\mathbb{F}_3[\lambda]/(\lambda^3+\lambda^2-\lambda+1)
F 3 3 = F 3 [ λ ] / ( λ 3 + λ 2 − λ + 1 )
F 3 3 \mathbb{F}_{3^3} F 3 3 即模p A ( λ ) p_A(\lambda) p A ( λ ) 的多项式环,其中系数的运算是在F 3 \mathbb{F}_3 F 3 上的,记F 3 3 \mathbb{F}_{3^3} F 3 3 上的变量是x x x (只是个符号而已);扩张后可以得到p A ( λ ) p_A(\lambda) p A ( λ ) 的三个不同解(原理未知,咕):
λ = x 3 0 , x 3 1 , x 3 2 ; i n F 3 3 \lambda=x^{3^0}, x^{3^1}, x^{3^2} ;\ \ in\ \mathbb{F}_{3^3}
λ = x 3 0 , x 3 1 , x 3 2 ; in F 3 3
化简一下就是:
λ = x , 2 x 2 + x + 2 , x 2 + x ; i n F 3 3 \lambda = x, 2x^2+x+2, x^2+x ;\ \ in\ \mathbb{F}_{3^3}
λ = x , 2 x 2 + x + 2 , x 2 + x ; in F 3 3
如果p A ( λ ) p_A(\lambda) p A ( λ ) 被分解成多个degree大于1的不可约多项式的话,λ \lambda λ 好像 就会分布在多个不同的域上;比如在F 3 \mathbb{F}_3 F 3 上有:
p A ( λ ) = λ 4 + 1 = 0 , i n F 3 p_A(\lambda)=\lambda^4+1=0, \ \ in\ \mathbb{F}_3
p A ( λ ) = λ 4 + 1 = 0 , in F 3
p A ( λ ) p_A(\lambda) p A ( λ ) 可以被分解为:
p A ( λ ) = ( λ 2 + λ + 2 ) ∗ ( λ 2 + 2 ∗ λ + 2 ) , i n F 3 p_A(\lambda) = (\lambda^2+\lambda+2) * (\lambda^2+2*\lambda+2), \ \ in\ \mathbb{F}_3
p A ( λ ) = ( λ 2 + λ + 2 ) ∗ ( λ 2 + 2 ∗ λ + 2 ) , in F 3
λ \lambda λ 就会分布在两个域上:F 3 [ λ ] / ( λ 2 + λ + 2 ) \mathbb{F}_3[\lambda]/(\lambda^2+\lambda+2) F 3 [ λ ] / ( λ 2 + λ + 2 ) 和F 3 [ λ ] / ( λ 2 + 2 ∗ λ + 2 ) \mathbb{F}_3[\lambda]/(\lambda^2+2*\lambda+2) F 3 [ λ ] / ( λ 2 + 2 ∗ λ + 2 ) ;
感觉上应该会有方法扩张到同一个更大的域上(咕
对角化·
对角化的方法基本上是一样的,特征值λ i \lambda_i λ i 各不相同的话就可以生成对角矩阵Λ \Lambda Λ ,有重根的话就可以生成Jordan矩阵J J J ;但像上面第4点说的,如果λ \lambda λ 分布在不同的域上的话会有问题(咕
Demo Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 if __name__ == '__main__' : p = 33285073849485750791903437807279991921 m = matrix(IntegerModRing(p), [ [1 , 1 , 1 ], [1 , 0 , 0 ], [0 , 1 , 0 ]]) print (factorFp(m.charpoly(), p)) B, J, Bi = jordanDiagon(m, p) print (p) print (J) print ('--- ---' ) q = 38845988283830087557982578789883120419 m = matrix(IntegerModRing(q), [ [1 , 1 , 1 ], [1 , 0 , 0 ], [0 , 1 , 0 ]]) print (factorFp(m.charpoly(), q)) B, J, Bi = jordanDiagon(m, q) print (p) print (J)
之所以是Demo Code是因为上面问题是没有解决的,即不是每个矩阵都能分解,但维度n ≤ 3 n \le 3 n ≤ 3 的话是都适用的;用了一个series 中的矩阵和e e e 分解的两个素数做例子,其中若p = 33285073849485750791903437807279991921 p=33285073849485750791903437807279991921 p = 33285073849485750791903437807279991921 的话,可以解出:
λ 1 = 13232791622035946436448165355007395754 , i n F p λ 2 = x , i n F p 2 λ 3 = 33285073849485750791903437807279991920 ∗ y + 20052282227449804355455272452272596168 , i n F p 2 \lambda_1 = 13232791622035946436448165355007395754, \ \ in\ \mathbb{F}_p \\
\lambda_2 = x, \ \ in\ \mathbb{F}_{p^2} \\
\lambda_3 = 33285073849485750791903437807279991920*y + 20052282227449804355455272452272596168, \ \ in\ \mathbb{F}_{p^2} \\
λ 1 = 13232791622035946436448165355007395754 , in F p λ 2 = x , in F p 2 λ 3 = 33285073849485750791903437807279991920 ∗ y + 20052282227449804355455272452272596168 , in F p 2
然后就可以构造出对角矩阵:
Λ = ( λ 1 0 0 0 λ 2 0 0 0 λ 3 ) \Lambda=
\begin{pmatrix}
\lambda_1 & 0 & 0 \\
0 & \lambda_2 & 0 \\
0 & 0 & \lambda_3 \\
\end{pmatrix}
Λ = ⎝ ⎛ λ 1 0 0 0 λ 2 0 0 0 λ 3 ⎠ ⎞
看上去同样是可以用这种方法,再求出B B B ,然后用B Λ k B − 1 B \Lambda^k B^{-1} B Λ k B − 1 加快求幂,但是要注意扩展域后是在F p 2 \mathbb{F}_{p^2} F p 2 上做的乘法运算;在最坏的情况下,是在F p n \mathbb{F}_{p^n} F p n 上(即p A ( λ ) p_A(\lambda) p A ( λ ) 是不可约的情况)做( n ∗ l o g ( k ) ) (n*log(k)) ( n ∗ l o g ( k )) 次乘法运算,一般的多项式乘法的复杂度是O ( n 2 ) O(n^2) O ( n 2 ) ,使用FFT 可优化到O ( n ∗ l o g ( n ) ) O(n*log(n)) O ( n ∗ l o g ( n )) ;即求A A A 的k k k 次幂可从原来的O ( n 2 + α ∗ l o g ( k ) ) O(n^{2+\alpha}*log(k)) O ( n 2 + α ∗ l o g ( k )) 优化到:
O ( n 2 ∗ l o g ( n ) ∗ l o g ( k ) ) O(n^2*log(n)*log(k))
O ( n 2 ∗ l o g ( n ) ∗ l o g ( k ))
所以其实这种优化是比较“看脸”的,而且据说已经有矩阵乘法的方法可以做到O ( n 2 ∗ l o g ( n ) ) O(n^2*log(n)) O ( n 2 ∗ l o g ( n )) 了[Wied14],不过如果p A ( λ ) p_A(\lambda) p A ( λ ) 是可约的话还是可以优化一下的(
既然现在知道G L ( n , F p ) GL(n, \mathbb{F}_p) G L ( n , F p ) 的对角化方法了,那么就进一步解释上次 计算G L ( n , F p ) GL(n, \mathbb{F}_p) G L ( n , F p ) 的阶的证明了(大概讲一下成因,详细的证明不会写,咕);另外这里也提供一种生成G L ( n , F p ) GL(n, \mathbb{F}_p) G L ( n , F p ) 的“最大阶”(p n − 1 p^n-1 p n − 1 )的方法:
证明的进一步解释·
首先 说一下“最大阶”的证明,当时的说法是:对于A ∈ G L ( n , F p ) A \in GL(n, \mathbb{F}_p) A ∈ G L ( n , F p ) 的话,最大的A A A 的阶是p n − 1 p^n-1 p n − 1 ,原因是p A ( λ ) p_A(\lambda) p A ( λ ) 最多n n n 项,减去全0 0 0 项的话最多就有p n − 1 p^n-1 p n − 1 种特征多项式;当时论坛 的那段回复后面还有一段:
大概意思就是p A ( λ ) p_A(\lambda) p A ( λ ) 的解(在阶最大的情况?)是在扩张后的域F p n \mathbb{F}_{p^n} F p n 上的,那么对角矩阵/Jordan矩阵的运算是在F p n \mathbb{F}_{p^n} F p n 上的,先看一下比较简单的对角矩阵的情况:由于F p n \mathbb{F}_{p^n} F p n 的阶就是p n − 1 p^n-1 p n − 1 ,所以Λ \Lambda Λ 在做p n − 1 p^n-1 p n − 1 次自乘后对角线上所有元素就会变为1 1 1 ,即:
Λ p n − 1 = I , i n F p n \Lambda^{p^n-1} = I\ , \ \ \ \ in\ \mathbb{F}_{p^n}
Λ p n − 1 = I , in F p n
Jordan矩阵的情况会比较复杂,经实验(懒得证了-),J J J 的上对角线上有1 1 1 的话,阶上就会产生因子p p p ,在奇素数的情况下应该可以证到阶会比p n − 1 p^n-1 p n − 1 小的?(注意如果p A ( λ ) p_A(\lambda) p A ( λ ) 不可约的话特征值是n n n 个F p n \mathbb{F}_{p^n} F p n 上的不同 值), 咕
再 说一下阶的证明,当时的说法是:
A p r ∏ i = 1 n ϕ i ( p ) ≡ I ( i n G L n ( Z p ) ) A^{p^r\prod_{i=1}^{n}\phi_i(p)} \equiv I \ \ (in\ GL_n(\mathbb{Z}_p))
A p r ∏ i = 1 n ϕ i ( p ) ≡ I ( in G L n ( Z p ))
先看ϕ i ( p ) \phi_i(p) ϕ i ( p ) 的那n n n 个因子,假设p A ( λ ) p_A(\lambda) p A ( λ ) 可以被分解为( n − i ) (n-i) ( n − i ) 个degree为1 1 1 的多项式和剩余的1 1 1 个不可约多项式,那么A A A 的特征值就有( n − i ) (n-i) ( n − i ) 个在F p \mathbb{F}_{p} F p 上和i i i 个在F p i \mathbb{F}_{p^i} F p i 上,于是如果是特征值互不相等的情况的话,就有:
Λ p i − 1 = I , i n F p i \Lambda^{p^i-1} = I\ , \ \ \ \ in\ \mathbb{F}_{p^i}
Λ p i − 1 = I , in F p i
(注意p i − 1 p^i-1 p i − 1 含因子p − 1 p-1 p − 1 ,也可看成F p \mathbb{F}_{p} F p 可以被扩张到F p i \mathbb{F}_{p^i} F p i );而ϕ i ( p ) \phi_i(p) ϕ i ( p ) 其实是p i p^i p i 的因子,所以就会有∏ i = 1 n ϕ i ( p ) \prod_{i=1}^{n}\phi_i(p) ∏ i = 1 n ϕ i ( p ) 这一块;
如果A A A 的特征值互不相等的话,就是大概对角化到一个Jordan矩阵,会产生若干的p p p 的因子,至于为什么是p r p^r p r 可以看下[MW97]的第三章。。。
最大阶的生成元·
A ∈ G L ( n , F p ) A \in GL(n, \mathbb{F}_p) A ∈ G L ( n , F p ) 的阶其实是和它的对角矩阵/Jordan矩阵的阶是一样的,按上面的说法,如果想达到最大阶的话,可以构造一个对角矩阵Λ \Lambda Λ ,其元素都是F p n \mathbb{F}_{p^n} F p n 上的,而且至少一个是F p n \mathbb{F}_{p^n} F p n 的原根,那么Λ \Lambda Λ 的阶就是p n − 1 p^n-1 p n − 1 ,即A A A 的阶也是p n − 1 p^n-1 p n − 1 ;
根据上面的说法,p A ( λ ) p_A(\lambda) p A ( λ ) 应该是一个F p \mathbb{F}_p F p 上不可约的多项式(而且p A ( λ ) = 0 p_A(\lambda)=0 p A ( λ ) = 0 至少有一个解是F p n \mathbb{F}_{p^n} F p n 的原根),在假设能找到这样的p A ( λ ) p_A(\lambda) p A ( λ ) 的前提下,可以通过以下方法找到G L ( n , F p ) GL(n, \mathbb{F}_p) G L ( n , F p ) 中一个阶为p n − 1 p^n-1 p n − 1 的元素:
构造p A ( λ ) p_A(\lambda) p A ( λ ) 的伴随矩阵 ,记为C C C :
选取随机R ∈ G L ( n , F p ) R \in GL(n, \mathbb{F}_p) R ∈ G L ( n , F p ) ,即可构造:
A = R C R − 1 A = RCR^{-1}
A = RC R − 1
根据伴随矩阵的性质,C C C 的n n n 个特征值就是p A ( λ ) p_A(\lambda) p A ( λ ) 对应的n n n 个解;根据上面说法,C C C 的阶是p n − 1 p^n-1 p n − 1 ;A ≌ C A≌C A ≌ C 即A A A 的阶和C C C 相同,也为p n − 1 p^n-1 p n − 1
Demo Code·
贴一个不太完善的参考代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 from collections import Countervar('x' ) def factorFp (f, p ): F.<a> = GF(p) R.<x> = PolynomialRing(F) return R(f).factor() def isIrreducible (f, p ): ft = factorFp(f, p) return len (ft)==1 and ft[0 ][1 ]==1 def getCharPoly (m ): mm = m.change_ring(ZZ).change_ring(SR) I = identity_matrix(mm.ncols()) return (mm-x*I).det().expand() def getLambdas (m, p ): cpm = m.charpoly() fs = factorFp(cpm, p) F.<a> = GF(p) R.<x> = PolynomialRing(F) lambdas = {F: []} try : for f in fs: for i in range (f[1 ]): deg = f[0 ].degree() if deg == 1 : lam = F(-(f[0 ](x=0 ))) try : lambdas[F].append(lam) except : lambdas[F] = [lam] else : S = R.quotient(f[0 ], 'y' ) lambdas[S] = [] lam = S(x) for d in range (deg): if d != 0 : lam = power(lam, p) lambdas[S].append(lam) return lambdas except Exception as e: print ('???' ) print (e) exit(0 ) def jordanDiagon (m, p ): n = m.ncols() lambdas = getLambdas(m, p) if len (lambdas) > 2 : raise NotImplementError("I don't know how to do that now ..." ) S = list (lambdas.keys())[-1 ] lams = [] for Si in lambdas: lams += [S(x) for x in lambdas[Si]] lambdas = lams lambdas.sort() lams = Counter(lambdas) I = identity_matrix(S, n) Js = [] evs = [] for lam in lams.keys(): numLam = lams[lam] m1 = m-lam*I numBlock = n - m1.rank() rs = [n] i = 1 while True : ri = (m1^i).rank() if ri == rs[-1 ]: rs.append(ri) break rs.append(ri) i += 1 jb = {} for i in range (len (rs))[1 :-1 ]: tmp = rs[i-1 ] + rs[i+1 ] - 2 *rs[i] if tmp != 0 : jb[i] = tmp for di in range (len (jb.keys())): d = list (jb.keys())[di] for k in range (jb[d]): ns = m1.right_kernel(basis='pivot' ) if d==1 : nb = ns.basis() evs.append(nb[di]) else : v0 = vector([0 for _ in range (n)]) while True : ev0 = ns.random_element() if ev0==v0 or ev0 in evs: continue tmp = [ev0] try : for i in range (1 , d): tmp.append(m1 \ vector(tmp[-1 ])) except : continue break evs += tmp Ji = diagonal_matrix(S, [lam for _ in range (d)]) jpos = 0 for j in range (d): if j != d-1 : Ji[jpos+j, jpos+j+1 ] = 1 Js.append(Ji) jpos += d J = zero_matrix(S, n) pos = 0 for Ji in Js: ni = Ji.ncols() for ri in range (ni): for ci in range (ni): tmp = Ji[ri, ci] if tmp != 0 : J[pos+ri, pos+ci] = tmp pos += ni B = matrix(S, evs).transpose() Binv = B.inverse() return B, J, Binv def randMp (p, n ): while True : m = matrix(IntegerModRing(p), [[randrange(p) for _ in range (n)] for _ in range (n)]) if m.det()!=0 : return m def companMp (cp, p ): F.<a> = GF(p) R.<x> = PolynomialRing(F) f = R(cp) n = f.degree() m = zero_matrix(F, n) for i in range (1 , n): m[i, i-1 ] = 1 for i in range (n): m[i, n-1 ] = -f[i] return m
[GSLA] Introduction to Linear Algebra, Fifth Edition (2016), http://math.mit.edu/~gs/linearalgebra/
[Ber70] Berlekamp E R. Factoring polynomials over large finite fields[J]. Mathematics of computation, 1970, 24(111): 713-735.
[MW97] Menezes A J, Wu Y H. The discrete logarithm problem in GL (n, q)[J]. Ars Combinatoria, 1997, 47: 23-32.
[Wied14] Wiedermann J. Fast nondeterministic matrix multiplication via derandomization of Freivalds’ algorithm[C]//IFIP International Conference on Theoretical Computer Science. Springer, Berlin, Heidelberg, 2014: 123-135.
[MA] Horn R A, Johnson C R. Matrix analysis[M]. Cambridge university press, 2012.