nth_root()
求解xm=kx^m=k

1
2
3
#x=k.nth_root(m)
x=27.nth_root(3)
x = mod(27, 2^120).nth_root(3)

这里也可以构造多项式求解,处理模下的问题时就把ZZ换成Zmod(n)而且要multiplicities=False,就是不需要计算重数。

1
2
3
P.<x>=PolynomialRing(ZZ)
f=x^m-k
x=f.roots(multiplicities=False)

线性代数

charpoly()
可以返回矩阵MM的特征多项式。

1
x=M.charpoly().roots(multiplicities=False)

和roots()函数结合就可以直接求得特征值。
eigenvectors
可以返回矩阵MM的特征值,特征向量,几何重数,分为eigenvectors_right()和eigenvectors_left()

1
x=M.eigenvectors_right()

返回MM的右特征向量也就是Mv=λvM v =\lambda v。x是一个三元组(v_list, λ, multiplicity)
solve_left()/solve_right()
分别用于求解Ax=bA \pmb{x}=\pmb{b}xA=b\pmb{x} A= \pmb{b}

1
x=M.solve_left(b) #x=M.solve_right(b)

companion_matrix(poly,format)
由一个单变量多项式创建一个伴随矩阵,矩阵的特征值就是矩阵的根。
poly接受多项式或者多项式系数
format接受四个值"right",“left”,“bottom”,"top"返回矩阵的不同形式

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
poly = [-2, -3, -4, -5, -6, 1]
R = companion_matrix(poly, format='right'); R
[0 0 0 0 2]
[1 0 0 0 3]
[0 1 0 0 4]
[0 0 1 0 5]
[0 0 0 1 6]
L = companion_matrix(poly, format='left'); L
[6 1 0 0 0]
[5 0 1 0 0]
[4 0 0 1 0]
[3 0 0 0 1]
[2 0 0 0 0]
B = companion_matrix(poly, format='bottom'); B
[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
[0 0 0 0 1]
[2 3 4 5 6]
T = companion_matrix(poly, format='top'); T
[6 5 4 3 2]
[1 0 0 0 0]
[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
perm = Permutation([5, 4, 3, 2, 1])
P = perm.to_matrix()
L == P*R*P
B == R.transpose()
T == P*R.transpose()*P

阿贝尔群

1
G = AbelianGroup([n])

定义:群运算满足交换律的群。

GF群

1
G = GF(p)

一般有两种,一个是加法群,一个是乘法群。
对于GF(p)GF(p)而言,加/乘法群就是模pp意义下的加/乘法。

GL群

1
G = GL(n,GF(p))

定义:在一个域 FF 上,所有可逆 n×nn \times n 矩阵在矩阵乘法下组成的群,记作:

GL(n,F)={AMn(F)    det(A)0}GL(n,F) = \{ A \in M_n(F) \;\mid\; \det(A) \neq 0 \}

显然GL群是非阿贝尔群。

群的阶

一个群GG的阶,就是群中元素的个数,记作G\lvert G \rvert
阿贝尔群的阶就是nn
GF加法群的阶是pp,而乘法群的阶是p1p-1
GL群的阶是i=0n1(qnqi)\prod_{i=0}^{n-1} \bigl(q^n - q^i\bigr)

元素的阶

对于设群GG的元素aa,若GG是乘法群,aa的阶就是指满足ak=ea^k=e方程的最小的kk,其中ee是群的单位元。
而加法群则是a×k=ea \times k=e方程的最小的k。

1
2
k=a.multiplicative_order() #乘法群阶
k=a.additive_order() #加法群阶

生成元

生成元是满足元素阶和群阶相同的元素

1
2
g = G.gens()
g0 = G.gen()

gens()可以返回群GG的所有生成元,gen()默认返回第一个。

1
g1 = G.gens()[1] #等价于g1 = gen(1)

特别注意的是阿贝尔群的生成元唯一会返回符号ff来表示。

ECC

在有限域GF(p)GF(p)上定义曲线y2=x3+ax+by^2 = x^3 + ax + b

1
E = EllipticCurve(GF(p), [a, b])

曲线群的阶(也就是点的个数)

1
g = E.order()

点运算

1
2
3
4
5
6
7
8
P = E(p_x,p_y) #点定义
Q = k*p #点乘法
R = P+Q #点加法
p = p.order() #点的阶
C = E.lift_x(c_x) #同理E.lift_y(c_y)
#这里的lift_x()函数默认返回负点,可以C=E.lift_x(c_x,all=True)这样会返回两个点用索引即可。
C0 = E.lift_x(c_x,all=True)[0]
C1 = E.lift_x(c_x,all=True)[1]

曲线群的生成元

1
G = E.gens()[0]

离散对数问题(P=pGP=p \cdot G,求p)

1
p = P.log(G) #或者p = discrete_log(P, G, operation="+")

妙妙工具

sage.matrix.berlekamp_massey.berlekamp_massey()
使用 Berlekamp-Massey 算法求解线性递归序列的最小多项式
线性递归序列 {ar}\{a_r\} 的最小多项式 gg 在定义上是指唯一的单项式多项式 gg
使得如果 {ar}\{a_r\} 满足线性递归aj+k+bj1aj1+k++b0ak=0(对所有 k0)a_{j+k} + b_{j-1} a_{j-1+k} + \cdots + b_0 a_k = 0 \quad (\text{对所有 } k \geq 0)gg 整除多项式xj+i=0j1bixi.x^j + \sum_{i=0}^{j-1} b_i x^i.
输入
长度为偶数的域(或域)元素列表
输出
序列的最小多项式,作为列表中元素所在域上的多项式

1
2
3
F=GF(2)
sage.matrix.berlekamp_massey.berlekamp_massey([F(1),F(2),F(1),F(2),F(1),F(2)])
#x^2+1

这个函数和前面线性代数部分提到的companion_matrix(poly,format) 函数配合就可以很方便求解lfsr问题(题目来源cryptohack–“L-WIN”)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
with open("lwin_output.txt") as f:
inp = f.read().strip()
F = GF(2)
# convert all the 1's and 0's to elements of GF(2)
l = [F(int(x)) for x in inp]
# run the Berlekamp-Massey algorithm.
# this function returns the characteristic polynomial for the LFSR.
g = sage.matrix.berlekamp_massey.berlekamp_massey(l)

# the companion matrix of the characteristic polynomial can be used to advance
# the LFSR: multiplying a vector of previous outputs by the matrix will give a
# vector whose last component is the next output.
M = companion_matrix(g, 'bottom')
v0 = vector(l[:384])

# since M is a matrix, we can invert it to run the LFSR in reverse: M^-768 is
# equivalent to running the LFSR backwards 768 iterations.
flag = M^-768 * v0

# and now just convert the 1's and 0's to ASCII
from Crypto.Util.number import long_to_bytes
print(long_to_bytes(int(''.join(str(x) for x in flag),2)))

对于左移型lfsrMM取"bottom",对于右移型lfsrMM取"top"。但是似乎不是所有的题目都能这样写,berlekamp_massey概率恢复出非整数的多项式,这个时候MM基本上是错的。