SymPy 1.13 中文文档(二十八)
原文:
docs.sympy.org/latest/index.html
数域
原文链接:
docs.sympy.org/latest/modules/polys/numberfields.html
引言
像代数数论中许多其他计算一样,有理素数的分裂只能用有理方法处理。如果考虑用自动计算设备进行计算,这一事实非常重要。只需知道不可约多项式(f(x)),其零点生成所讨论的域即可。
—Olga Taussky,1953
像数域和代数数这样的概念对我们理解代数数论至关重要,但对于计算机而言,这个主题完全是关于多项式的:在(\mathbb{Q}[x])上通过不可约多项式(f(x) \in \mathbb{Q}[x])降阶。因此,它在 SymPy 的polys
模块下找到了自然的归属。
多位作者(如 Taussky、Zimmer、Pohst 和 Zassenhaus,或者 Cohen)以不同的方式阐明了计算代数数论的主要目标,但无论如何,列表都围绕着一定的基本任务集中。作为 SymPy 中numberfields
模块的目标,我们可以根据[Cohen93],第 4.9.3 节,列出以下列表。
对于数域(K = \mathbb{Q}(\theta)),其代数整数环标记为(\mathbb{Z}_K),计算:
-
(\mathbb{Z}_K)的一个整数基底
-
(\mathbb{Z}_K)中有理素数的分解
-
对于理想和元素的(\mathfrak{p})-adic 赋值
-
(K)的 Galois 封闭的 Galois 群
-
(K)的一组基本单位
-
规则(R(K))
-
类数
-
类群(Cl(K))的结构
-
决定一个给定理想是否为主理想,如果是,则计算一个生成元。
作为基础,并支持我们定义和处理数域和代数数的基本能力,我们也根据[Cohen93],第 4.5 节,设置以下问题。
-
给定一个代数数,可以通过根式和有理运算表达,甚至可以作为超越函数的特殊值,确定它在(\mathbb{Q})上的最小多项式。
-
子域问题:通过它们的生成元(\alpha)和(\beta)的最小多项式,给定两个数域(\mathbb{Q}(\alpha)),(\mathbb{Q}(\beta)),决定一个数域是否同构于另一个数域的子域,并展示嵌入。
-
域成员问题:给定两个代数数(\alpha),(\beta),决定(\alpha \in \mathbb{Q}(\beta))是否成立,如果成立,则写成(\alpha = f(\beta)),其中(f(x) \in \mathbb{Q}[x])。
-
原初元问题:给定几个代数数(\alpha_1, \ldots, \alpha_m),计算一个单一的代数数(\theta),使得(\mathbb{Q}(\alpha_1, \ldots, \alpha_m) = \mathbb{Q}(\theta))。
目前,SymPy 仅支持上述任务的子集,如果您有兴趣扩展支持,请贡献!提供所有剩余问题解决方案(以及已解决的问题)的出色来源是 [Cohen93]。
在写作时,上述问题的现有解决方案可以在以下位置找到:
任务 | 实现 |
---|
|
- 整数基础
round_two() |
---|
|
- 素数分解
prime_decomp() |
---|
|
- (\mathfrak{p})-递值
prime_valuation() |
---|
|
- Galois 群
galois_group() |
---|
|
- 寻找最小多项式
minimal_polynomial() |
---|
|
- 子领域
field_isomorphism() |
---|
|
- 领域成员
to_number_field() |
---|
|
- 原始元素
primitive_element() |
---|
解决主要问题
整数基础
sympy.polys.numberfields.basis.round_two(T, radicals=None)
Zassenhaus 的“Round 2” 算法。
参数:
T : Poly
, AlgebraicField
要么是在 ZZ 或 QQ 上的不可约多项式,定义数域,或者是代表数域本身的
AlgebraicField
。
根式 : dict, optional
这是任何 (p)-根式(如果计算的话)通过引用返回的一种方式。如果需要,传递一个空字典。如果算法达到计算环 (Z_K) 的 nilradical mod (p) 的点,则该字典中将以
p
为键存储此理想的 (\mathbb{F}_p)-基础。这对于其他算法(如素数分解)可能很有用。
返回:
对 (ZK, dK)
,其中:
ZK
是表示最大秩的Submodule
。
dK
是字段 (K = \mathbb{Q}[x]/(T(x))) 的判别式。
说明
在 ZZ 或 QQ 上的不可约多项式T上执行 Zassenhaus 的“Round 2”算法。这计算了一个整数基和字段(K = \mathbb{Q}[x]/(T(x)))的判别式。
或者,您可以传递一个AlgebraicField
实例,替代多项式T,在这种情况下算法应用于该字段的原始元的最小多项式。
通常不必直接调用此函数,因为可以访问maximal_order()
、integral_basis()
和discriminant()
方法的AlgebraicField
。
示例
通过 AlgebraicField 进行操作:
>>> from sympy import Poly, QQ
>>> from sympy.abc import x
>>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
>>> K = QQ.alg_field_from_poly(T, "theta")
>>> print(K.maximal_order())
Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2
>>> print(K.discriminant())
-503
>>> print(K.integral_basis(fmt='sympy'))
[1, theta, theta/2 + theta**2/2]
直接调用:
>>> from sympy import Poly
>>> from sympy.abc import x
>>> from sympy.polys.numberfields.basis import round_two
>>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
>>> print(round_two(T))
(Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2, -503)
在第二轮算法中有时会计算模(p)的零根式,这可能在进一步计算中很有用。传递一个字典在(radicals)下接收这些:
>>> T = Poly(x**3 + 3*x**2 + 5)
>>> rad = {}
>>> ZK, dK = round_two(T, radicals=rad)
>>> print(rad)
{3: Submodule[[-1, 1, 0], [-1, 0, 1]]}
另见
AlgebraicField.maximal_order
,AlgebraicField.integral_basis
,AlgebraicField.discriminant
参考文献
[R802]
Cohen, H. 计算代数数论课程.
质数分解
sympy.polys.numberfields.primes.prime_decomp(p, T=None, ZK=None, dK=None, radical=None)
计算在一个数域中有理质数p的分解。
参数:
p:int
想要分解的有理质数。
T:Poly
,可选
定义数域(K)中的单一不可约多项式。注意:T或ZK至少要提供一个。
ZK:Submodule
,可选
如果已知,(K)的最大序。注意:T或ZK至少要提供一个。
dK:int,可选
如果已知,(K)的判别式。
radical:Submodule
,可选
如果已知,(K)的整数中的模p的零根式。
返回:
PrimeIdeal
实例列表。
解释
通常应通过 AlgebraicField
的 primes_above()
方法访问它。
示例
>>> from sympy import Poly, QQ
>>> from sympy.abc import x, theta
>>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
>>> K = QQ.algebraic_field((T, theta))
>>> print(K.primes_above(2))
[[ (2, x**2 + 1) e=1, f=1 ], [ (2, (x**2 + 3*x + 2)/2) e=1, f=1 ],[ (2, (3*x**2 + 3*x)/2) e=1, f=1 ]]
参考文献
[R803]
Cohen, H. 计算代数数论课程. (见算法 6.2.9.)
class sympy.polys.numberfields.primes.PrimeIdeal(ZK, p, alpha, f, e=None)
一个代数整数环中的素理想。
__init__(ZK, p, alpha, f, e=None)
参数:
ZK:Submodule
此理想所在的最大阶。
p:int
此理想除以的有理素数。
alpha:PowerBasisElement
使得理想等于
p*ZK + alpha*ZK
。
f:int
惯性度。
e:int,None
,可选
如果已知分歧指数。 如果为
None
,我们将在此计算它。
__add__(other)
转换为 Submodule
并添加到另一个 Submodule
中。
另请参阅
as_submodule
__mul__(other)
转换为 Submodule
并乘以另一个 Submodule
或有理数。
另请参阅
as_submodule
as_submodule()
将此素理想表示为 Submodule
。
返回:
Submodule
将等于
self.p * self.ZK + self.alpha * self.ZK
。
解释
PrimeIdeal
类用于捆绑关于素理想的信息,如其惯性度、分歧指数和双生成器表示,以及提供像 valuation()
和 test_factor()
这样有用的方法。
然而,为了能够与其他理想或有理数相加和相乘,首先必须将其转换为一个支持这些操作的 Submodule
类。
在许多情况下,用户不需要刻意执行这种转换,因为算术运算符方法__add__()
和__mul__()
会自动执行此操作。
将一个PrimeIdeal
提升到非负整数次方也是支持的。
示例
>>> from sympy import Poly, cyclotomic_poly, prime_decomp
>>> T = Poly(cyclotomic_poly(7))
>>> P0 = prime_decomp(7, T)[0]
>>> print(P0**6 == 7*P0.ZK)
True
请注意,在上述等式的两边,我们有一个Submodule
。在下一个等式中,我们回顾到添加理想值会产生它们的最大公约数。这次,我们需要在右边有一个明确的转换为Submodule
:
>>> print(P0 + 7*P0.ZK == P0.as_submodule())
True
另见
__add__
, __mul__
property is_inert
说清楚我们在整数环中保留的有理素数是否惰性,即保持原样。
reduce_ANP(a)
将一个ANP
减少到此素理想的“小代表”。
参数:
elt:ANP
要减少的元素。
返回:
ANP
减少后的元素。
另见
reduce_element
, reduce_alg_num
, Submodule.reduce_element
reduce_alg_num(a)
将一个AlgebraicNumber
减少到此素理想的“小代表”。
参数:
elt:AlgebraicNumber
要减少的元素。
返回:
AlgebraicNumber
减少后的元素。
另见
reduce_element
,reduce_ANP
,Submodule.reduce_element
reduce_element(elt)
将PowerBasisElement
减少到模这个素数理想的“小代表”。
参数:
elt:PowerBasisElement
要减少的元素。
返回:
PowerBasisElement
被减少的元素。
参见
reduce_ANP
,reduce_alg_num
,Submodule.reduce_element
repr(field_gen=None, just_gens=False)
打印这个素数理想的表示。
参数:
field_gen:Symbol
,None
,可选(默认为 None
)
用于场的生成器的符号。这将出现在我们对
self.alpha
的表示中。如果为None
,我们使用self.ZK
的定义多项式的变量。
just_gens:布尔值,可选(默认为 False
)
如果为
True
,仅打印“(p, alpha)”部分,显示素数理想的“生成器”。否则,打印形式为“[ (p, alpha) e=…, f=… ]”的字符串,给出分歧指数和惯性度,以及生成器。
示例
>>> from sympy import cyclotomic_poly, QQ
>>> from sympy.abc import x, zeta
>>> T = cyclotomic_poly(7, x)
>>> K = QQ.algebraic_field((T, zeta))
>>> P = K.primes_above(11)
>>> print(P[0].repr())
[ (11, x**3 + 5*x**2 + 4*x - 1) e=1, f=3 ]
>>> print(P[0].repr(field_gen=zeta))
[ (11, zeta**3 + 5*zeta**2 + 4*zeta - 1) e=1, f=3 ]
>>> print(P[0].repr(field_gen=zeta, just_gens=True))
(11, zeta**3 + 5*zeta**2 + 4*zeta - 1)
test_factor()
计算这个素数理想的一个测试因子。
解释
为这个素数理想写下(\mathfrak{p}),(p)是它分解的有理素数。然后,为了计算(\mathfrak{p})-递减估值,有一个数(\beta \in \mathbb{Z}_K),使得(p/\mathfrak{p} = p \mathbb{Z}_K + \beta \mathbb{Z}_K)非常有用。
本质上,这与库默(Kummer)1847 年的论文(Ueber die Zerlegung…, Crelle vol. 35)中的数\Psi(或“试剂”)相同,其中发明了理想除法。
valuation(I)
计算整数理想 I 在这个素数理想上的(\mathfrak{p})-递减估值。
参数:
I:Submodule
参见
prime_valuation
p-递减估值
sympy.polys.numberfields.primes.prime_valuation(I, P)
计算整数理想 I 的P-递减估值。
参数:
I : Submodule
欲求其价值的整数理想。
P : PrimeIdeal
计算价值的质数。
返回:
int
示例
>>> from sympy import QQ
>>> from sympy.polys.numberfields import prime_valuation
>>> K = QQ.cyclotomic_field(5)
>>> P = K.primes_above(5)
>>> ZK = K.maximal_order()
>>> print(prime_valuation(25*ZK, P[0]))
8
亦见:
PrimeIdeal.valuation
参考
[R804]
Cohen, H. A Course in Computational Algebraic Number Theory. (见算法 4.8.17。)
Galois 群
sympy.polys.numberfields.galoisgroups.galois_group(f, *gens, by_name=False, max_tries=30, randomize=False, **args)
计算多项式 f 的 Galois 群,直到 6 次。
参数:
f : 表达式
不可约多项式在 ZZ 或 QQ 上,其 Galois 群需确定。
gens : 符号的可选列表
将 f 转换为 Poly,并传递给
poly_from_expr()
函数。
by_name : bool,默认为 False
如果
True
,则以名称返回 Galois 群。否则将其作为PermutationGroup
返回。
max_tries : int,默认为 30
在生成 Tschirnhausen 变换的步骤中最多尝试这么多次。
randomize : bool,默认为 False
如果为
True
,则在生成 Tschirnhausen 变换时使用随机系数。否则,按固定顺序尝试变换。这两种方法都从小系数和次数开始,并向上工作。
args : 可选
将 f 转换为 Poly,并传递给
poly_from_expr()
函数。
返回:
对 (G, alt)
进行配对
第一个元素
G
表示 Galois 群。它是sympy.combinatorics.galois.S1TransitiveSubgroups
和sympy.combinatorics.galois.S2TransitiveSubgroups
等枚举类的实例,如果by_name
是True
,则返回其名称;如果是False
,则返回一个PermutationGroup
。第二个元素是一个布尔值,表明群是否包含于交错群 (A_n) ((n) 是 T 的次数)。
引发:
ValueError
如果 f 的度数不支持。
MaxTriesException
如果在生成 Tschirnhausen 变换时尝试次数超过 max_tries 则无法完成。
示例
>>> from sympy import galois_group
>>> from sympy.abc import x
>>> f = x**4 + 1
>>> G, alt = galois_group(f)
>>> print(G)
PermutationGroup([
(0 1)(2 3),
(0 2)(1 3)])
该组返回时附带一个布尔值,指示它是否包含在交替群 (A_n) 中,其中 (n) 是 T 的次数。结合其他群属性,这有助于确定它是哪一个群:
>>> alt
True
>>> G.order()
4
另外,可以通过名称返回该组:
>>> G_name, _ = galois_group(f, by_name=True)
>>> print(G_name)
S4TransitiveSubgroups.V
然后可以通过调用名称的 get_perm_group()
方法获得该组:
>>> G_name.get_perm_group()
PermutationGroup([
(0 1)(2 3),
(0 2)(1 3)])
群名称是枚举类sympy.combinatorics.galois.S1TransitiveSubgroups
,sympy.combinatorics.galois.S2TransitiveSubgroups
等的值。
另请参见
Poly.galois_group
查找最小多项式
sympy.polys.numberfields.minpoly.minimal_polynomial(ex, x=None, compose=True, polys=False, domain=None)
计算代数元素的最小多项式。
参数:
ex : 表达式
要计算其最小多项式的元素或表达式。
x : 符号,可选
最小多项式的自变量
compose : 布尔值,可选(默认值=True)
用于计算最小多项式的方法。如果
compose=True
(默认值),则使用_minpoly_compose
,如果compose=False
,则使用格罗本基。
polys : 布尔值, 可选(默认值=False)
如果
True
返回Poly
对象,否则返回Expr
对象。
domain : 域,可选
基域
注意
默认情况下 compose=True
,计算 ex
的子表达式的最小多项式,然后使用结果和因式分解对它们进行算术运算。如果 compose=False
,则使用 groebner
的自下而上算法。默认算法更少停滞。
如果没有指定基域,将从表达式中自动生成。
示例
>>> from sympy import minimal_polynomial, sqrt, solve, QQ
>>> from sympy.abc import x, y
>>> minimal_polynomial(sqrt(2), x)
x**2 - 2
>>> minimal_polynomial(sqrt(2), x, domain=QQ.algebraic_field(sqrt(2)))
x - sqrt(2)
>>> minimal_polynomial(sqrt(2) + sqrt(3), x)
x**4 - 10*x**2 + 1
>>> minimal_polynomial(solve(x**3 + x + 3)[0], x)
x**3 + x + 3
>>> minimal_polynomial(sqrt(y), x)
x**2 - y
sympy.polys.numberfields.minpoly.minpoly(ex, x=None, compose=True, polys=False, domain=None)
这是minimal_polynomial()
的同义词。
子域问题
polys.numberfields.subfield
中的函数解决“子域问题”和相关问题,适用于代数数域。
根据 Cohen(参见[Cohen93] 第 4.5 节),我们可以将主要问题定义如下:
-
子域问题:
给定两个数域 (\mathbb{Q}(\alpha)),(\mathbb{Q}(\beta)),通过它们生成元 (\alpha) 和 (\beta) 的最小多项式,判断一个数域是否同构于另一个数域的子域。
从解决此问题的解中,流出以下问题的解:
-
原始元素问题:
给定几个代数数 (\alpha_1, \ldots, \alpha_m),计算一个单一的代数数 (\theta),使得 (\mathbb{Q}(\alpha_1, \ldots, \alpha_m) = \mathbb{Q}(\theta))。
-
域同构问题:
判断两个数域 (\mathbb{Q}(\alpha)),(\mathbb{Q}(\beta)) 是否同构。
-
域成员问题:
给定两个代数数 (\alpha)、(\beta),决定 (\alpha \in \mathbb{Q}(\beta)),如果是,则写成 (\alpha = f(\beta)) 其中 (f(x) \in \mathbb{Q}[x])。
sympy.polys.numberfields.subfield.field_isomorphism(a, b, *, fast=True)
在一个数域嵌入到另一个数域中。
参数:
a : Expr
任何表示代数数的表达式。
b : Expr
任何表示代数数的表达式。
fast : 布尔值,可选(默认为 True)
如果为
True
,首先尝试一种可能更快的计算同构的方法,如果失败,则退回到较慢的方法。如果为False
,则直接使用保证返回结果的较慢方法。
返回:
有理数列表,或者为 None
如果 (\mathbb{Q}(a)) 不同构于 (\mathbb{Q}(b)) 的某个子域,则返回
None
。否则,返回一个有序列表,表示 (\mathbb{Q}(b)) 中元素,其中 (a) 可能被映射到,以定义单同态,即从 (\mathbb{Q}(a)) 到 (\mathbb{Q}(b)) 的同构。列表的元素是 (b) 的降幂的系数。
解释
此函数寻找从 (\mathbb{Q}(a)) 到 (\mathbb{Q}(b)) 的同构。因此,它解决了子域问题。
示例
>>> from sympy import sqrt, field_isomorphism, I
>>> print(field_isomorphism(3, sqrt(2)))
[3]
>>> print(field_isomorphism( I*sqrt(3), I*sqrt(3)/2))
[2, 0]
sympy.polys.numberfields.subfield.primitive_element(extension, x=None, *, ex=False, polys=False)
找到由几个生成元给出的数域的单一生成元。
参数:
extension : Expr
的列表
每个表达式必须表示代数数 (\alpha_i)。
x : Symbol
,可选(默认为 None)
所需出现在原始元素 (\theta) 的计算最小多项式中的符号。如果为
None
,则使用虚拟符号。
ex : 布尔值,可选(默认为 False)
当且仅当为
True
时,计算每个 (\alpha_i) 的表示,作为 (\theta) 的幂次在 (\mathbb{Q}) 上的线性组合。
polys : 布尔值,可选(默认为 False)
如果为
True
,将最小多项式作为Poly
返回。否则将其作为Expr
返回。
返回:
一对 (f, coeffs) 或三元组 (f, coeffs, reps),其中:
f
是原始元素的最小多项式。coeffs
给出原始元素作为给定生成元的线性组合。如果传递了ex=True
,则reps
存在,并且是有理数列表的列表。每个列表给出原始生成元的降幂系数,以恢复其中之一。
解释
基本问题是:给定几个代数数 (\alpha_1, \alpha_2, \ldots, \alpha_n),找到一个单一的代数数 (\theta),使得 (\mathbb{Q}(\alpha_1, \alpha_2, \ldots, \alpha_n) = \mathbb{Q}(\theta))。
此函数实际上保证 (\theta) 将是 (\alpha_i) 的线性组合,其系数为非负整数。
此外,如果需要,此函数将告诉您如何将每个 (\alpha_i) 表示为 (\theta) 的 (\mathbb{Q}) 线性组合的幂次元素。
Examples
>>> from sympy import primitive_element, sqrt, S, minpoly, simplify
>>> from sympy.abc import x
>>> f, lincomb, reps = primitive_element([sqrt(2), sqrt(3)], x, ex=True)
然后 lincomb
告诉我们原始元素作为给定生成元 sqrt(2)
和 sqrt(3)
的线性组合。
>>> print(lincomb)
[1, 1]
这意味着原始元素是 (\sqrt{2} + \sqrt{3})。同时 f
是这个原始元素的最小多项式。
>>> print(f)
x**4 - 10*x**2 + 1
>>> print(minpoly(sqrt(2) + sqrt(3), x))
x**4 - 10*x**2 + 1
最后,reps
(仅因为我们设置了关键字参数 ex=True
而返回)告诉我们如何将每个生成元 (\sqrt{2}) 和 (\sqrt{3}) 作为 (\mathbb{Q}) 线性组合的幂次元素 (\sqrt{2} + \sqrt{3}) 的恢复方式。
>>> print([S(r) for r in reps[0]])
[1/2, 0, -9/2, 0]
>>> theta = sqrt(2) + sqrt(3)
>>> print(simplify(theta**3/2 - 9*theta/2))
sqrt(2)
>>> print([S(r) for r in reps[1]])
[-1/2, 0, 11/2, 0]
>>> print(simplify(-theta**3/2 + 11*theta/2))
sqrt(3)
sympy.polys.numberfields.subfield.to_number_field(extension, theta=None, *, gen=None, alias=None)
在生成的域中表达一个代数数。
Parameters:
extension : Expr
或者 Expr
的列表
要么是要在另一个域中表达的代数数,要么是代数数的列表,其中一个原始元素将在另一个域中表达。
theta : Expr
, None, optional (default=None)
如果代表代数数的
Expr
,则其行为如 Explanation 中所述。如果为None
,则此函数简化为对extension
调用primitive_element()
并将计算得到的原始元素转换为AlgebraicNumber
。
gen : Symbol
, None, optional (default=None)
如果提供,则将用作返回的
AlgebraicNumber
的最小多项式的生成符号。
alias : str, Symbol
, None, optional (default=None)
如果提供,则将用作返回的
AlgebraicNumber
的别名符号。
Returns:
AlgebraicNumber
属于 (\mathbb{Q}(\theta)) 并等于 (\eta)。
Raises:
IsomorphismFailed
如果 (\eta \not\in \mathbb{Q}(\theta))。
Explanation
给定两个代数数 (\eta, \theta),此函数要么将 (\eta) 表示为 (\mathbb{Q}(\theta)) 中的元素,要么在 (\eta \not\in \mathbb{Q}(\theta)) 时引发异常。
此函数本质上只是一种方便的工具,利用 field_isomorphism()
(我们解决的子域问题)来解决这个域成员问题。
作为额外方便,此函数允许您传递代数数列 (\alpha_1, \alpha_2, \ldots, \alpha_n) 而不是 (\eta)。它会使用 primitive_element()
对 (\alpha_i) 列表计算 (\eta),作为原始元问题的解。
示例
>>> from sympy import sqrt, to_number_field
>>> eta = sqrt(2)
>>> theta = sqrt(2) + sqrt(3)
>>> a = to_number_field(eta, theta)
>>> print(type(a))
<class 'sympy.core.numbers.AlgebraicNumber'>
>>> a.root
sqrt(2) + sqrt(3)
>>> print(a)
sqrt(2)
>>> a.coeffs()
[1/2, 0, -9/2, 0]
我们得到一个 AlgebraicNumber
,其 .root
是 (\theta),其值是 (\eta),并且其 .coeffs()
显示如何用 (\theta) 的降幂写出 (\eta) 的 (\mathbb{Q})-线性组合。
另见
field_isomorphism
,primitive_element
内部
代数数域
在 SymPy 中,代数数域由 AlgebraicField
类表示,这是 多项式域系统 的一部分。
表示代数数
表示代数数有多种不同的方式,对于不同的计算任务可能有不同的首选形式。参见 [Cohen93],第 4.2 节。
作为数域元素
在 SymPy 中,有一个区分数字和表达式类别的区别,分别在 sympy.core.numbers
模块中定义,在 polys
模块中定义域和域元素。这在 这里 有更详细的解释。
当涉及代数数时,sympy.core.numbers
模块提供 AlgebraicNumber
类,而 polys
模块提供 ANP
类。这是属于 AlgebraicField
域的域元素类型。
作为有限生成模块的元素
在计算代数数论中,有限生成的(\mathbb{Z})-模块具有至关重要的作用。例如,每个秩和每个理想都是这样一个模块。
特别地,在数域中,最大秩的序数——或整数环——是一个有限生成的(\mathbb{Z})-模块,其生成器形成该域的整基。
允许我们表示这种模块及其元素的类,都在modules
模块中提供。在这里,ModuleElement
类提供了另一种表示代数数的方式。
有限生成模块
数域中的模块。
这里定义的类允许我们处理有限生成的自由模块,其生成器是代数数。
还有一个名为Module
的抽象基类,它有两个具体子类,PowerBasis
和Submodule
。
每个模块由其基础或生成器集合定义:
-
对于
PowerBasis
,生成器是代数整数(\theta)的前(n)个幂(从零开始),其次数为(n)。PowerBasis
通过传递(\theta)的最小多项式或将(\theta)作为其原始元素的AlgebraicField
来构造。 -
对于
Submodule
,生成器是另一个模块的(\mathbb{Q})-线性组合的集合。那个模块则是Submodule
的“父”模块。(\mathbb{Q})-线性组合的系数可以由整数矩阵和正整数分母给出。矩阵的每一列定义一个生成器。
>>> from sympy.polys import Poly, cyclotomic_poly, ZZ
>>> from sympy.abc import x
>>> from sympy.polys.matrices import DomainMatrix, DM
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> T = Poly(cyclotomic_poly(5, x))
>>> A = PowerBasis(T)
>>> print(A)
PowerBasis(x**4 + x**3 + x**2 + x + 1)
>>> B = A.submodule_from_matrix(2 * DomainMatrix.eye(4, ZZ), denom=3)
>>> print(B)
Submodule[[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]/3
>>> print(B.parent)
PowerBasis(x**4 + x**3 + x**2 + x + 1)
因此,每个模块都是PowerBasis
或Submodule
的一个祖先,如果S
是一个Submodule
,那么它的祖先是S.parent
、S.parent.parent
等。
ModuleElement
类表示任何模块的生成器的线性组合。关键是,这个线性组合的系数不限于整数,而可以是任何有理数。这是必要的,以便任何和所有的代数整数都可以表示,从某个数域中的原始元素(\theta)的幂开始。例如,在二次域(\mathbb{Q}(\sqrt{d}))中,其中(d \equiv 1 \mod{4}),需要一个分母为 2。
ModuleElement
可以由整数列向量和分母构造:
>>> U = Poly(x**2 - 5)
>>> M = PowerBasis(U)
>>> e = M(DM([[1], [1]], ZZ), denom=2)
>>> print(e)
[1, 1]/2
>>> print(e.module)
PowerBasis(x**2 - 5)
PowerBasisElement
类是ModuleElement
的子类,表示PowerBasis
的元素,并添加了直接在原始元素(\theta)的幂上表示的元素相关的功能。
与模块元素进行算术运算
虽然ModuleElement
表示特定模块的生成器的线性组合,但要记住每个模块都是PowerBasis
或其后代(沿着Submodule
对象的链),因此实际上每个ModuleElement
都表示某个域(\mathbb{Q}(\theta))中的代数数,其中(\theta)是某个PowerBasis
的定义元素。因此,讨论给定的ModuleElement
属于哪个数域是有意义的。
这意味着任意两个ModuleElement
实例都可以相加、相减、相乘或相除,只要它们属于同一个数域。同样,由于 (\mathbb{Q}) 是每个数域的子域,任何ModuleElement
也可以与任何有理数相加、相乘等。
>>> from sympy import QQ
>>> from sympy.polys.numberfields.modules import to_col
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> C = A.submodule_from_matrix(3 * DomainMatrix.eye(4, ZZ))
>>> e = A(to_col([0, 2, 0, 0]), denom=3)
>>> f = A(to_col([0, 0, 0, 7]), denom=5)
>>> g = C(to_col([1, 1, 1, 1]))
>>> e + f
[0, 10, 0, 21]/15
>>> e - f
[0, 10, 0, -21]/15
>>> e - g
[-9, -7, -9, -9]/3
>>> e + QQ(7, 10)
[21, 20, 0, 0]/30
>>> e * f
[-14, -14, -14, -14]/15
>>> e ** 2
[0, 0, 4, 0]/9
>>> f // g
[7, 7, 7, 7]/15
>>> f * QQ(2, 3)
[0, 0, 0, 14]/15
然而,在对ModuleElement
进行算术操作时必须要小心,因为结果所属的模块 (C) 将是两个操作数所属的模块 (A) 和 (B) 的最近公共祖先(NCA),而 (C) 可能与 (A) 和 (B) 中的任意一个或两个都不同。
>>> A = PowerBasis(T)
>>> B = A.submodule_from_matrix(2 * DomainMatrix.eye(4, ZZ))
>>> C = A.submodule_from_matrix(3 * DomainMatrix.eye(4, ZZ))
>>> print((B(0) * C(0)).module == A)
True
在执行算术操作之前,两个操作数的副本会自动转换为 NCA 的元素(操作数本身不会被修改)。沿祖先链的向上转换非常简单:只需依次乘以每个Submodule
的定义矩阵即可。
反过来,也支持向下转换,即将给定的ModuleElement
表示为子模块中的一个元素,具体方法是represent()
方法。但是一般来说,这种转换并不保证总是成功,因为给定的元素可能不属于子模块。这个问题通常在乘法时出现,因为模块在加法下封闭,但不一定在乘法下封闭。
乘法
一般来说,一个模块不一定在乘法下封闭,即不一定构成一个环。然而,在数域的背景下我们处理的许多模块实际上是环,而我们的类确实支持乘法。
具体来说,任何Module
都可以尝试计算其自己的乘法表,但只有在尝试将属于它的两个ModuleElement
实例相乘时才会这样做。
>>> A = PowerBasis(T)
>>> print(A._mult_tab is None)
True
>>> a = A(0)*A(1)
>>> print(A._mult_tab is None)
False
每个PowerBasis
本质上都是在乘法下封闭的,因此PowerBasis
的实例总是可以成功地计算它们的乘法表。
当一个Submodule
试图计算其乘法表时,它会将自己的每个生成元转换为其父模块中的元素,并在那里进行每个可能的配对的乘法运算,然后试图将结果表示为其自身的(\mathbb{Z})-线性组合,即其自身的生成元。只有当子模块实际上在乘法下是封闭的时,这个过程才会成功。
模同态
许多重要的数论算法需要计算一个或多个模同态的核。因此,我们有几个轻量级类,ModuleHomomorphism
,ModuleEndomorphism
,InnerEndomorphism
和 EndomorphismRing
,它们提供了支持这些操作所需的最小机制。
类参考
class sympy.polys.numberfields.modules.Module
通用的有限生成模块。
这是一个抽象基类,不应直接实例化。两个具体的子类是PowerBasis
和 Submodule
。
每个Submodule
都来源于另一个模块,通过其parent
属性引用。如果S
是一个子模块,则我们称S.parent
,S.parent.parent
等为S
的“祖先”。因此,每个Module
要么是一个PowerBasis
,要么是一个Submodule
,其中某个祖先是PowerBasis
。
__call__(spec, denom=1)
生成属于此模块的ModuleElement
。
参数:
spec : DomainMatrix
, int
指定
ModuleElement
的系数的分子。可以是整数向量,范围为 ZZ,其长度必须等于此模块生成器的数量(n),或者是整数j
,(0 \leq j < n),表示单位矩阵(I_n)的第(n \times n)列的简写。
denom:整数,可选(默认=1)
ModuleElement
的系数的分母。
返回:
ModuleElement
系数是spec向量的条目,除以denom。
示例
>>> from sympy.polys import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.modules import PowerBasis, to_col
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> e = A(to_col([1, 2, 3, 4]), denom=3)
>>> print(e)
[1, 2, 3, 4]/3
>>> f = A(2)
>>> print(f)
[0, 0, 1, 0]
ancestors(include_self=False)
返回此模块的祖先模块列表,从基础的PowerBasis
开始向下,可选包括self
。
另见
Module
basis_elements()
获取ModuleElement
生成器列表。
element_from_rational(a)
返回一个表示有理数的ModuleElement
。
参数:
a:整数,ZZ,QQ
返回:
ModuleElement
解释
返回的ModuleElement
将属于此模块的祖先链上的第一个模块(包括此模块本身),该链以单位元素开始。
示例
>>> from sympy.polys import Poly, cyclotomic_poly, QQ
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> a = A.element_from_rational(QQ(2, 3))
>>> print(a)
[2, 0, 0, 0]/3
endomorphism_ring()
形成此模块的EndomorphismRing
。
is_compat_col(col)
判断col是否适合此模块的列向量。
mult_tab()
获取此模块的乘法表(如果在乘法下封闭)。
返回:
字典的字典的列表
抛出:
ClosureFailure
如果模块在乘法下不封闭。
解释
计算表示乘法表上三角部分的字典M
的字典的列表。
换句话说,如果0 <= i <= j < self.n
,那么M[i][j]
是系数列表c
,使得g[i] * g[j] == sum(c[k]*g[k], k in range(self.n))
,其中g
是此模块生成器的列表。
如果j < i
,则M[i][j]
未定义。
示例
>>> from sympy.polys import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> print(A.mult_tab())
{0: {0: [1, 0, 0, 0], 1: [0, 1, 0, 0], 2: [0, 0, 1, 0], 3: [0, 0, 0, 1]},1: {1: [0, 0, 1, 0], 2: [0, 0, 0, 1], 3: [-1, -1, -1, -1]},2: {2: [-1, -1, -1, -1], 3: [1, 0, 0, 0]},3: {3: [0, 1, 0, 0]}}
property n
此模块的生成器数量。
nearest_common_ancestor(other)
定位此模块及另一个模块的最近公共祖先。
返回:
Module
,None
另见
Module
property number_field
返回关联的AlgebraicField
(如果有的话)。
返回:
AlgebraicField
,None
解释
可以在Poly
(f)上或者在AlgebraicField
(K)上构造PowerBasis
。在后一种情况下,PowerBasis
及其所有后代模块将返回(K)作为其.number_field
属性,而在前一种情况下,它们将全部返回None
。
one()
返回表示单位的ModuleElement
,并属于该模块的第一个祖先(包括它自身),该祖先以单位开始。
property parent
如果有的话,返回此模块的父模块。
返回:
Module
,None
解释
对于Submodule
,这是其parent
属性;对于PowerBasis
,这是None
。
另请参见
Module
power_basis_ancestor()
返回这个模块的祖先的PowerBasis
。
另请参见
Module
represent(elt)
将模块元素表示为该模块生成器的整数线性组合。
参数:
elt:ModuleElement
要表示的模块元素。 必须属于此模块的某个祖先模块(包括此模块本身)。
返回:
DomainMatrix
关于 ZZ
这将是一个列向量,表示该模块生成器的线性组合的系数,其等于给定的元素。
引发:
ClosureFailure
如果给定的元素不能表示为此模块上的 ZZ 线性组合。
解释
在我们的系统中,“表示”始终意味着将ModuleElement
写成当前Module
的生成元的 ZZ 线性组合。此外,传入的ModuleElement
必须属于当前Module
的祖先(或当前Module
本身)。
最常见的应用是在Submodule
中表示ModuleElement
。例如,这涉及计算乘法表。
另一方面,在PowerBasis
中表示是一个特例,这种情况通常不会在实践中出现,除非例如在ModuleEndomorphism
在PowerBasis
上使用时。
在这种情况下,(1)传入的ModuleElement
必须属于PowerBasis
本身(因为后者没有适当的祖先),(2)它“可表示”当且仅当它属于(\mathbb{Z}[\theta])(尽管通常PowerBasisElement
可以表示(\mathbb{Q}(\theta))中的任何元素,即任何代数数)。
示例
>>> from sympy import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.modules import PowerBasis, to_col
>>> from sympy.abc import zeta
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> a = A(to_col([2, 4, 6, 8]))
ModuleElement
a
的所有系数都是偶数。如果我们在子模块B = 2*A
中表示a
,列向量中的系数将减半:
>>> B = A.submodule_from_gens([2*A(i) for i in range(4)])
>>> b = B.represent(a)
>>> print(b.transpose())
DomainMatrix([[1, 2, 3, 4]], (1, 4), ZZ)
然而,如此定义的B
中的元素仍表示相同的代数数:
>>> print(a.poly(zeta).as_expr())
8*zeta**3 + 6*zeta**2 + 4*zeta + 2
>>> print(B(b).over_power_basis().poly(zeta).as_expr())
8*zeta**3 + 6*zeta**2 + 4*zeta + 2
另请参阅
Submodule.represent
,PowerBasis.represent
starts_with_unity()
判断模块的第一个生成元是否等于单位元。
submodule_from_gens(gens, hnf=True, hnf_modulus=None)
用属于本模块的 ModuleElement
列表生成的子模。
参数:
gens : 属于本模块的 ModuleElement
列表。
hnf : boolean, optional (默认=True)
如果为 True,则在形成
Submodule
前将矩阵缩减为 Hermite 正规形式。
hnf_modulus : int, None, optional (默认=None)
用于 HNF 缩减算法中的模数。参见
hermite_normal_form()
。
返回:
Submodule
示例
>>> from sympy.polys import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> gens = [A(0), 2*A(1), 3*A(2), 4*A(3)//5]
>>> B = A.submodule_from_gens(gens)
>>> print(B)
Submodule[[5, 0, 0, 0], [0, 10, 0, 0], [0, 0, 15, 0], [0, 0, 0, 4]]/5
另请参阅
submodule_from_matrix
submodule_from_matrix(B, denom=1)
用矩阵的列指示的本模块的元素生成的子模,带有可选的分母。
参数:
B : ZZ
上的 DomainMatrix
。
每一列都给出了子模的一个生成元的系数的分子。因此,B 的行数必须等于当前模的生成元的数量。
denom : int, optional (默认=1)
所有生成元的公共分母的最小公倍数。
返回:
Submodule
引发:
ValueError
如果给定的矩阵 B 不是在 ZZ 上或其行数不等于当前模的生成元的数量。
示例
>>> from sympy.polys import Poly, cyclotomic_poly, ZZ
>>> from sympy.polys.matrices import DM
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> B = A.submodule_from_matrix(DM([
... [0, 10, 0, 0],
... [0, 0, 7, 0],
... ], ZZ).transpose(), denom=15)
>>> print(B)
Submodule[[0, 10, 0, 0], [0, 0, 7, 0]]/15
另请参阅
submodule_from_gens
whole_submodule()
返回一个等于整个模的子模。
说明
当您有一个 PowerBasis
并希望将其转换为 Submodule
(以使用后者属于的方法)时,这将非常有用。
zero()
返回一个表示零的 ModuleElement
。
class sympy.polys.numberfields.modules.PowerBasis(T)
由代数整数的幂生成的模。
__init__(T)
参数:
T : Poly
, AlgebraicField
(1)整数环上的首一、不可约、一元多项式,其根是幂基的生成元,或者(2)一个
代数域
,其原始元素是幂基的生成元。
element_from_ANP(a)
将 ANP 转换为幂基元素。
element_from_alg_num(a)
将代数数转换为幂基元素。
element_from_poly(f)
在我们定义的最小多项式模除后,产生此模块的元素,表示 f。
参数:
f : 多项式
over ZZ,与我们定义的多项式相同的变量。
返回:
幂基元素
represent(elt)
将模块元素表示为该模块生成器上的整数线性组合。
另请参阅
Module.represent
,Submodule.represent
class sympy.polys.numberfields.modules.Submodule(parent, matrix, denom=1, mult_tab=None)
另一个模块的子模块。
__init__(parent, matrix, denom=1, mult_tab=None)
参数:
parent : 模块
派生此模块的模块。
matrix : 域矩阵
over ZZ
其列定义此子模块生成器作为父生成器上的线性组合的矩阵。
denom : 整数, 可选 (默认为 1)
矩阵给定系数的分母。
mult_tab : 字典, None
, 可选
如果已知,可以提供此模块的乘法表。
property QQ_matrix
域矩阵
over QQ,等于 self.matrix / self.denom
,并保证为稠密矩阵。
返回:
域矩阵
over QQ
解释
取决于其形成方式,域矩阵
可能具有内部表示为稀疏或稠密的表现形式。我们在这里保证稠密表示,以便子模块的等价性测试总是符合预期。
示例
>>> from sympy.polys import Poly, cyclotomic_poly, ZZ
>>> from sympy.abc import x
>>> from sympy.polys.matrices import DomainMatrix
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> T = Poly(cyclotomic_poly(5, x))
>>> A = PowerBasis(T)
>>> B = A.submodule_from_matrix(3*DomainMatrix.eye(4, ZZ), denom=6)
>>> C = A.submodule_from_matrix(DomainMatrix.eye(4, ZZ), denom=2)
>>> print(B.QQ_matrix == C.QQ_matrix)
True
add(other, hnf=True, hnf_modulus=None)
将此 子模块
添加到另一个模块中。
参数:
other : 子模块
hnf : 布尔值, 可选 (默认为 True)
如果为
True
,将组合模块的矩阵减少到其 Hermite 正常形式。
hnf_modulus:ZZ,可选,默认为 None
如果提供了正整数,则在 HNF 约简中使用它作为模数。参见
hermite_normal_form()
。
返回:
子模块
说明
这代表由两个模块生成器集合的并集生成的模块。
basis_element_pullbacks()
返回此子模块作为父模块中的基元素列表。
discard_before(r)
通过丢弃给定索引r之前的所有生成器来生成一个新的模块。
mul(other, hnf=True, hnf_modulus=None)
将此子模块
乘以一个有理数,一个模元素
或另一个子模块
。
参数:
other:整数,ZZ,QQ,模元素
,子模块
hnf:布尔值,可选(默认为 True)
如果为
True
,将产品模块的矩阵减少到其 Hermite 正常形式。
hnf_modulus:ZZ,可选,默认为 None
如果提供了正整数,则在 HNF 约简中使用它作为模数。参见
hermite_normal_form()
。
返回:
子模块
说明
乘以有理数或模元素
意味着形成其生成器为当前子模块所有生成器与此数量的乘积的子模块。
乘以另一个子模块
意味着形成其生成器为一个子模块的一个生成器与另一个子模块的一个生成器的乘积的子模块。
reduce_element(elt)
如果此子模块(B)在方形、最大秩 Hermite 正常形式中具有定义矩阵(W),则给定父模块(A)的元素(x),我们产生一个元素(y \in A),使得(x - y \in B),并且(y)的第(i)个坐标满足(0 \leq y_i < w_{i,i})。这个代表(y)是唯一的,意味着每个余类(x + B)在此过程下都将其减少为它。
参数:
元素 : 模块元素
这个子模块的父模块的一个元素。
返回:
元素 : 模块元素
这个子模块的父模块的一个元素。
引发:
未实现错误
如果给定的
模块元素
不属于此子模块的父模块。
结构错误
如果这个子模块的定义矩阵不是方的、最大秩的 Hermite 正则形式。
解释
特别情况下,当 (A) 是数域 (K) 的一个幂基,而 (B) 是代表理想 (I) 的一个子模块时,这个操作代表了将 (K) 中的一个元素对 (I) 取模以得到一个“小”代表的几种重要方法之一。参见 [Cohen00] 第 1.4.3 节。
示例
>>> from sympy import QQ, Poly, symbols
>>> t = symbols('t')
>>> k = QQ.alg_field_from_poly(Poly(t**3 + t**2 - 2*t + 8))
>>> Zk = k.maximal_order()
>>> A = Zk.parent
>>> B = (A(2) - 3*A(0))*Zk
>>> B.reduce_element(A(2))
[3, 0, 0]
参考文献
[Cohen00] (1,2)
Cohen, H. 计算数论的高级主题.
reduced()
生成这个子模块的简化版本。
返回:
子模块
解释
在简化版本中,保证 1 是唯一同时整除子模块的分母和子模块矩阵中每个条目的正整数。
represent(elt)
将一个模块元素表示为该模块生成元的整数线性组合。
另请参阅
Module.represent
, PowerBasis.represent
class sympy.polys.numberfields.modules.ModuleElement(module, col, denom=1)
表示 模块
的一个元素。
注:不应直接构造。请使用 __call__()
方法或 make_mod_elt()
工厂函数代替。
__init__(module, col, denom=1)
参数:
模块 : 模块
属于此元素的模块。
列 : 域矩阵
over ZZ
给出此元素系数的分子的列向量。
分母 : int, 可选 (默认=1)
此元素的系数的分母。
__add__(other)
一个ModuleElement
可以加到一个有理数,或者另一个ModuleElement
。
解释
当另一个加数是有理数时,它将被转换为一个ModuleElement
(属于这个模块的第一个以单位开始的祖先)。
在所有情况下,和属于两个加数模块的最近公共祖先(NCA)。如果 NCA 不存在,我们返回NotImplemented
。
__mul__(other)
一个ModuleElement
可以乘以有理数,或者另一个ModuleElement
。
解释
当乘数是一个有理数时,该产品通过直接操作这个ModuleElement
的系数来计算。
当乘数是另一个ModuleElement
时,该产品将属于两个操作数的模块的最近公共祖先(NCA),并且该 NCA 必须有一个乘法表。如果 NCA 不存在,我们返回NotImplemented
。如果 NCA 没有乘法表,将会引发ClosureFailure
。
__mod__(m)
将这个ModuleElement
对Submodule
进行归约。
参数:
m:整数,ZZ,QQ,Submodule
如果是
Submodule
,相对于此归约self
。如果是整数或有理数,则相对于我们自己模块乘以这个常数的Submodule
进行归约。
参见
Submodule.reduce_element
property QQ_col
DomainMatrix
在 QQ 上,等于self.col / self.denom
,并且保证是密集的。
参见
Submodule.QQ_matrix
column(domain=None)
获取此元素列的副本,可选择转换为域。
equiv(other)
如果它们表示相同的代数数,则ModuleElement
可能测试为等价于有理数或另一个。
参数:
other:int,ZZ,QQ,ModuleElement
返回:
布尔值
引发:
UnificationFailed
如果
self
和other
不共享PowerBasis
祖先。
解释
此方法仅意在检查等价性的情况下进行测试;即,当other是可以与此一致化的ModuleElement
(即,与此共享公共PowerBasis
祖先)或者是一个有理数时(因为每个PowerBasis
代表每个有理数)。
classmethod from_int_list(module, coeffs, denom=1)
从整数列表(而不是列向量)创建一个ModuleElement
。
is_compat(other)
测试other是否为另一个具有相同模块的ModuleElement
。
property n
此元素列的长度。
over_power_basis()
转换为我们PowerBasis
祖先上的PowerBasisElement
。
reduced()
生成此ModuleElement
的简化版本,即分母和所有分子系数的 gcd 为 1。
reduced_mod_p(p)
生成此ModuleElement
的版本,其中所有分子系数已经对p取模。
to_ancestor(anc)
将其转换为属于此元素模块祖先的ModuleElement
。
参数:
anc:Module
to_parent()
转换为此元素模块父级的ModuleElement
。
unify(other)
尝试生成一个与ModuleElement
兼容的配对,一个等效于这个,另一个等效于另一个。
返回:
配对(e1, e2)
每个
ei
是ModuleElement
,它们属于同一个Module
,e1
等效于self
,e2
等效于other
。
抛出:
UnificationFailed
如果
self
和other
没有共同的祖先模块。
Explanation
我们搜索元素对的最近共同祖先模块,并在那里表示每个元素。
class sympy.polys.numberfields.modules.PowerBasisElement(module, col, denom=1)
子类,用于模块为PowerBasis
的ModuleElement
实例。
property T
访问PowerBasis
的定义多项式。
as_expr(x=None)
从self
创建一个基本表达式。
property generator
返回一个Symbol
,用于在多项式中表示此元素。
如果我们有一个关联的AlgebraicField
,其原始元素具有别名符号,我们使用它。否则,我们使用定义功率基底的极小多项式的变量。
property is_rational
判断此元素是否表示一个有理数。
norm(T=None)
计算该数的范数。
numerator(x=None)
将该数的分子作为多项式获取在 ZZ 上。
poly(x=None)
将该数作为多项式获取在 QQ 上。
to_ANP()
转换为等效的ANP
。
to_alg_num()
尝试转换为等效的AlgebraicNumber
。
返回:
AlgebraicNumber
抛出:
StructureError
如果这个元素所属的
PowerBasis
没有关联的AlgebraicField
。
Explanation
一般来说,从AlgebraicNumber
到PowerBasisElement
的转换会丢失信息,因为AlgebraicNumber
指定了一个复数嵌入,而PowerBasisElement
则没有。然而,在某些情况下,可以将PowerBasisElement
转换回AlgebraicNumber
,特别是当相关的PowerBasis
引用了一个AlgebraicField
时。
sympy.polys.numberfields.modules.make_mod_elt(module, col, denom=1)
工厂函数用于构建一个ModuleElement
,但如果模块是PowerBasis
,则确保它是PowerBasisElement
。
class sympy.polys.numberfields.modules.ModuleHomomorphism(domain, codomain, mapping)
从一个模块到另一个模块的同态映射。
__init__(domain, codomain, mapping)
参数:
domain:Module
映射的定义域。
codomain:Module
映射的值域。
mapping:可调用对象
任意可调用对象都可以被接受,但应选择代表实际模块同态的对象。特别是,应该接受定义域的元素并返回值域的元素。
示例:
>>> from sympy import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.modules import PowerBasis, ModuleHomomorphism
>>> T = Poly(cyclotomic_poly(5))
>>> A = PowerBasis(T)
>>> B = A.submodule_from_gens([2*A(j) for j in range(4)])
>>> phi = ModuleHomomorphism(A, B, lambda x: 6*x)
>>> print(phi.matrix())
DomainMatrix([[3, 0, 0, 0], [0, 3, 0, 0], [0, 0, 3, 0], [0, 0, 0, 3]], (4, 4), ZZ)
kernel(modulus=None)
计算代表这个同态的核心的子模块。
参数:
modulus:int,可选
如果核心应该在模数
p
下计算,则为正的素数(p)。
返回:
Submodule
这个子模块的生成元跨越在 ZZ 上这个同态的核心,或者如果给定了模数,那么在 GF(p)上跨越。
matrix(modulus=None)
计算这个同态的矩阵。
参数:
modulus:int,可选
如果矩阵应该在模数
p
下被约简,则为正的素数(p)。
返回:
DomainMatrix
如果矩阵是在 ZZ 上,否则是在 GF(p)上,如果给定了模数。
class sympy.polys.numberfields.modules.ModuleEndomorphism(domain, mapping)
从一个模到其自身的同态映射。
__init__(domain, mapping)
参数:
定义域:Module
映射的公共定义域和值域。
映射:可调用
接受任意可调用对象,但应选择以表示实际的模自同态。特别地,应接受并返回 定义域 的元素。
class sympy.polys.numberfields.modules.InnerEndomorphism(domain, multiplier)
模上的内部自同态,即乘以固定元素所对应的自同态。
__init__(domain, multiplier)
参数:
定义域:Module
自同态的定义域和值域。
乘子:ModuleElement
元素 (a) 定义映射为 (x \mapsto a x)。
class sympy.polys.numberfields.modules.EndomorphismRing(domain)
一个模的自同态环。
__init__(domain)
参数:
定义域:Module
自同态的定义域和值域。
inner_endomorphism(multiplier)
形成属于这个自同态环的内部自同态。
参数:
乘子:ModuleElement
定义内部自同态的元素 (a),即 (x \mapsto a x)。
返回:
InnerEndomorphism
represent(element)
将这个自同态环的元素表示为一个单列向量。
参数:
元素:ModuleEndomorphism
属于这个环。
返回:
DomainMatrix
列向量等于将表示给定 元素 的矩阵的所有列竖直堆叠。
解释
设 (M) 是一个模,(E) 是其自同态环。设 (N) 是另一个模,并考虑一个同态 (\varphi: N \rightarrow E)。如果 (\varphi) 要用矩阵 (A) 表示,那么 (A) 的每一列必须表示 (E) 的一个元素。当 (E) 的元素本身可以表示为矩阵时,可以通过将这样一个矩阵的列堆叠成单列来实现这一点。
此方法支持计算这样的矩阵 (A),首先将这个自同态环的一个元素表示为矩阵,然后将该矩阵的列堆叠成单列。
示例
注意,在这些示例中,我们打印矩阵的转置,以便更容易检查其列。
>>> from sympy import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.modules import PowerBasis
>>> from sympy.polys.numberfields.modules import ModuleHomomorphism
>>> T = Poly(cyclotomic_poly(5))
>>> M = PowerBasis(T)
>>> E = M.endomorphism_ring()
设 (\zeta) 是一个原始的五次单位根,是我们域的生成元,考虑由 (\zeta) 引出的整数环上的内部自同态 (\tau):
>>> zeta = M(1)
>>> tau = E.inner_endomorphism(zeta)
>>> tau.matrix().transpose()
DomainMatrix([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [-1, -1, -1, -1]],(4, 4), ZZ)
矩阵表示的 (\tau) 如预期所示。第一列显示乘以 (\zeta) 将 (1) 带到 (\zeta),第二列显示将 (\zeta) 带到 (\zeta²),依此类推。
环 E
的同态的 represent
方法将它们堆叠到单列中:
>>> E.represent(tau).transpose()
DomainMatrix([[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1]],(1, 16), ZZ)
当我们想要考虑一个具有 E
作为余域的同态 (\varphi) 时,这是有用的:
>>> phi = ModuleHomomorphism(M, E, lambda x: E.inner_endomorphism(x))
我们希望计算这样一个同态的矩阵:
>>> phi.matrix().transpose()
DomainMatrix([[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1],[0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1, 1, 0, 0, 0],[0, 0, 0, 1, -1, -1, -1, -1, 1, 0, 0, 0, 0, 1, 0, 0]],(4, 16), ZZ)
注意,(\tau) 的堆叠矩阵在此示例中作为第二列出现。这是因为 (\zeta) 是 M
的第二个基元素,并且 (\varphi(\zeta) = \tau)。
sympy.polys.numberfields.modules.find_min_poly(alpha, domain, x=None, powers=None)
找到一个在有单位元的有限生成环中满足一个元素的最小次数(不一定是不可约的)的多项式。
参数:
alpha : ModuleElement
期望找到其最小多项式的元素,并且其模具有乘法并以单位元开始。
domain : Domain
多项式的期望域。
x : Symbol
,可选
多项式的期望变量。
powers : 列表,可选
如果需要,传递空列表。从零到最小多项式的次数,将 alpha 的幂(作为
ModuleElement
实例)记录在此处,我们在计算它们时。
返回:
Poly
,None
对于 alpha 的最小多项式,如果在期望的域上找不到多项式,则返回
None
。
引发:
MissingUnityError
如果 alpha 属于的模不以单位元开头。
ClosureFailure
如果 alpha 属于的模不闭合于乘法。
示例
对于第 (n) 个旋缠域,(n) 为奇素数,考虑其根为长度为 ((n-1)/2) 的两个周期的二次方程。高斯的文章 356 告诉我们,根据 (n) 是 1 或 3 模 4,我们应该得到 (x² + x - (n-1)/4) 或 (x² + x + (n+1)/4)。
>>> from sympy import Poly, cyclotomic_poly, primitive_root, QQ
>>> from sympy.abc import x
>>> from sympy.polys.numberfields.modules import PowerBasis, find_min_poly
>>> n = 13
>>> g = primitive_root(n)
>>> C = PowerBasis(Poly(cyclotomic_poly(n, x)))
>>> ee = [g**(2*k+1) % n for k in range((n-1)//2)]
>>> eta = sum(C(e) for e in ee)
>>> print(find_min_poly(eta, QQ, x=x).as_expr())
x**2 + x - 3
>>> n = 19
>>> g = primitive_root(n)
>>> C = PowerBasis(Poly(cyclotomic_poly(n, x)))
>>> ee = [g**(2*k+2) % n for k in range((n-1)//2)]
>>> eta = sum(C(e) for e in ee)
>>> print(find_min_poly(eta, QQ, x=x).as_expr())
x**2 + x + 5
实用程序
sympy.polys.numberfields.utilities.is_rat(c)
测试参数是否适合用作有理数。
解释
对于类型为 int
、ZZ 或 QQ 的任何参数返回 True
。
另请参阅
is_int
sympy.polys.numberfields.utilities.is_int(c)
测试参数是否适合用作整数。
解释
对于类型为 int
或 ZZ 的任何参数返回 True
。
另请参阅
is_rat
sympy.polys.numberfields.utilities.get_num_denom(c)
给定 is_rat()
返回为 True
的任何参数,返回此数的分子和分母。
另见
is_rat
sympy.polys.numberfields.utilities.extract_fundamental_discriminant(a)
从整数 a 中提取基本判别式。
参数:
a: int, 必须是 0 或 1 模 4
返回:
字典对 (D, F)
。
抛出:
ValueError
如果 a 不是 4 的倍数余 0 或 1.
解释
给定任意 0 或 1 模 4 的有理整数 a,写成 (a = d f²),其中 (d) 要么是 1 要么是基本判别式,并返回给定的由 factorint()
返回格式相同的字典对 (D, F)
,分别给出 (d) 和 (f) 的素因子分解。
基本判别式 (d) 不同于单位,并且要么是 4 的倍数余 1 并且无平方因子,要么是 4 的倍数余 0 并且 (d/4) 是无平方因子并且余 2 或 3。这等同于某些二次域的判别式。
示例
>>> from sympy.polys.numberfields.utilities import extract_fundamental_discriminant
>>> print(extract_fundamental_discriminant(-432))
({3: 1, -1: 1}, {2: 2, 3: 1})
作为比较:
>>> from sympy import factorint
>>> print(factorint(-432))
{2: 4, 3: 3, -1: 1}
参考文献
[R805]
Cohen, H. 计算代数数论课程.(见 Prop. 5.1.3)
class sympy.polys.numberfields.utilities.AlgIntPowers(T, modulus=None)
计算代数整数的幂。
解释
给定一个代数整数 (\theta) 由其在 ZZ 上的首一不可约多项式 T
,此类计算 (\theta) 的任意高次幂的表示,作为 ZZ 上的线性组合,其中 (n = \deg(T))。
利用多项式 T
导出的 (\theta) 的幂的线性递归关系计算表示。见 [1],第 4.2.2 节。
可选地,可以将表示与模数减少。
示例
>>> from sympy import Poly, cyclotomic_poly
>>> from sympy.polys.numberfields.utilities import AlgIntPowers
>>> T = Poly(cyclotomic_poly(5))
>>> zeta_pow = AlgIntPowers(T)
>>> print(zeta_pow[0])
[1, 0, 0, 0]
>>> print(zeta_pow[1])
[0, 1, 0, 0]
>>> print(zeta_pow[4])
[-1, -1, -1, -1]
>>> print(zeta_pow[24])
[-1, -1, -1, -1]
参考文献
[R806]
Cohen, H. 计算代数数论课程.
__init__(T, modulus=None)
参数:
T : Poly
定义代数整数的首一不可约多项式在 ZZ 上。
modulus : int, None, 可选
如果不是
None
,所有表示将被减少到这个。
sympy.polys.numberfields.utilities.coeff_search(m, R)
为搜索多项式生成系数。
参数:
m : int
系数列表的长度。
R : int
系数的初始最大绝对值(搜索进行时将增加)。
返回:
generator
列表系数的无限生成器。
解释
导数系数始终为非负。在增加界限前,探索绝对值受限的所有组合。跳过全零列表,跳过任何重复。见示例。
示例
>>> from sympy.polys.numberfields.utilities import coeff_search
>>> cs = coeff_search(2, 1)
>>> C = [next(cs) for i in range(13)]
>>> print(C)
[[1, 1], [1, 0], [1, -1], [0, 1], [2, 2], [2, 1], [2, 0], [2, -1], [2, -2],[1, 2], [1, -2], [0, 2], [3, 3]]
sympy.polys.numberfields.utilities.supplement_a_subspace(M)
将子空间的基扩展为整个空间的基。
参数:
M : DomainMatrix
列给出子空间的基。
返回:
DomainMatrix
此矩阵是可逆的,其前 (r) 列等于 M。
提升:
DMRankError
如果 M 不是最大秩。
解释
给定一个秩为 (r) 的 (n \times r) 矩阵 M(因此 (r \leq n)),此函数计算一个可逆的 (n \times n) 矩阵 (B),使得 (B) 的前 (r) 列等于 M。
这个操作可以解释为扩展子空间的基,从而给出整个空间的基。
具体来说,假设您有一个 (n)-维向量空间 (V),其基为 ({v_1, v_2, \ldots, v_n}),以及 (V) 的一个 (r)-维子空间 (W),其由基 ({w_1, w_2, \ldots, w_r}) 张成,其中 (w_j) 被给出作为 (v_i) 的线性组合。如果 M 的列表示这些 (w_j) 作为这种线性组合,则此函数计算的矩阵 (B) 的列给出一个新的基 ({u_1, u_2, \ldots, u_n}) 用于 (V),再次相对于 ({v_i}) 基,并且对于 (1 \leq j \leq r),有 (u_j = w_j)。
示例
注意:该函数按列处理,因此在这些示例中,我们打印矩阵的转置,以便更容易检查列。
>>> from sympy.polys.matrices import DM
>>> from sympy import QQ, FF
>>> from sympy.polys.numberfields.utilities import supplement_a_subspace
>>> M = DM([[1, 7, 0], [2, 3, 4]], QQ).transpose()
>>> print(supplement_a_subspace(M).to_Matrix().transpose())
Matrix([[1, 7, 0], [2, 3, 4], [1, 0, 0]])
>>> M2 = M.convert_to(FF(7))
>>> print(M2.to_Matrix().transpose())
Matrix([[1, 0, 0], [2, 3, -3]])
>>> print(supplement_a_subspace(M2).to_Matrix().transpose())
Matrix([[1, 0, 0], [2, 3, -3], [0, 1, 0]])
参考资料
[R807]
Cohen, H. 计算代数数论课程(见第 2.3.2 节。)
sympy.polys.numberfields.utilities.isolate(alg, eps=None, fast=False)
为实数代数数找到一个有理隔离区间。
参数:
alg:str, int, Expr
要隔离的代数数。必须是实数,才能使用此特定函数。但是,另请参阅
Poly.intervals()
,当您传递all=True
时,它可以隔离复数根。
eps:正元素 QQ 中的元素,None,可选(默认为 None)
要传递给
Poly.refine_root()
的精度。
fast:布尔值,可选(默认为 False)
说明是否应该使用快速细化过程。(将传递给
Poly.refine_root()
.)
返回:
一对有理数,定义给定代数数的隔离区间
代数数。
示例
>>> from sympy import isolate, sqrt, Rational
>>> print(isolate(sqrt(2)))
(1, 2)
>>> print(isolate(sqrt(2), eps=Rational(1, 100)))
(24/17, 17/12)
另请参阅
Poly.intervals
范畴论
原文链接:
docs.sympy.org/latest/modules/categories.html
引言
SymPy 的范畴论模块将允许在单一范畴内操作图表,包括在 TikZ 中绘制它们并判断它们是否可交换。
此模块试图遵循的一般参考工作是
[JoyOfCats]
- Adamek, H. Herrlich. G. E. Strecker: 抽象和具体范畴。猫的乐趣。
最新版本的这本书可以免费下载。
katmat.math.uni-bremen.de/acc/acc.pdf
此模块仍处于前胚阶段。
基类参考
此部分列出了实现范畴论基本概念的类别的类:对象,态射,范畴和图表。
class sympy.categories.Object(name, **assumptions)
任何抽象范畴中任何类型对象的基类。
解释
尽管技术上任何 Basic
的实例都可以,但这个类是在抽象范畴中创建抽象对象的推荐方式。
class sympy.categories.Morphism(domain, codomain)
抽象范畴中任何态射的基类。
解释
在抽象范畴中,态射是两个范畴对象之间的箭头。箭头起点的对象称为定义域,而终点的对象称为值域。
相同对象之间的两个态射被认为是相同的态射。要区分相同对象之间的态射,请使用 NamedMorphism
。
禁止实例化此类。请改用其中一个派生类。
另见
IdentityMorphism
, NamedMorphism
, CompositeMorphism
property codomain
返回态射的值域。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> f = NamedMorphism(A, B, "f")
>>> f.codomain
Object("B")
compose(other)
与提供的态射组合自身。
构成组合的元素顺序是通常的顺序,即构造 (g\circ f) 使用 g.compose(f)
。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> g * f
CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"),
NamedMorphism(Object("B"), Object("C"), "g")))
>>> (g * f).domain
Object("A")
>>> (g * f).codomain
Object("C")
property domain
返回态射的定义域。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> f = NamedMorphism(A, B, "f")
>>> f.domain
Object("A")
class sympy.categories.NamedMorphism(domain, codomain, name)
表示具有名称的态射。
解释
名称用于区分具有相同定义域和值域的态射:如果它们具有相同的定义域,值域和名称,则两个命名的态射相等。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> f = NamedMorphism(A, B, "f")
>>> f
NamedMorphism(Object("A"), Object("B"), "f")
>>> f.name
'f'
另见
Morphism
property name
返回态射的名称。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> f = NamedMorphism(A, B, "f")
>>> f.name
'f'
class sympy.categories.CompositeMorphism(*components)
表示一个由其他态射组成的态射。
解释
如果它们从中获得的态射(组件)相同并以相同顺序列出,则两个复合态射相等。
此类的构造函数的参数应按图表顺序列出:从 Morphism
实例 g
和 f
获取复合态射 (g\circ f)。
例子
>>> from sympy.categories import Object, NamedMorphism, CompositeMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> g * f
CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"),
NamedMorphism(Object("B"), Object("C"), "g")))
>>> CompositeMorphism(f, g) == g * f
True
property codomain
返回这个复合态射的余域。
复合态射的余域是其最后组成部分的余域。
例子
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> (g * f).codomain
Object("C")
property components
返回此复合态射的组件。
例子
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> (g * f).components
(NamedMorphism(Object("A"), Object("B"), "f"),
NamedMorphism(Object("B"), Object("C"), "g"))
property domain
返回这个复合态射的定义域。
复合态射的定义域是其第一个组成部分的定义域。
例子
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> (g * f).domain
Object("A")
flatten(new_name)
忘记了这个态射的复合结构。
解释
如果 new_name
不为空,则返回一个带有提供名称的 NamedMorphism
,否则返回一个 Morphism
。在两种情况下,新态射的定义域是此复合态射的定义域,新态射的余域是此复合态射的余域。
例子
>>> from sympy.categories import Object, NamedMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> (g * f).flatten("h")
NamedMorphism(Object("A"), Object("C"), "h")
class sympy.categories.IdentityMorphism(domain)
表示一个恒等态射。
解释
恒等态射是具有相同定义域和余域的态射,其在复合方面作为恒等元素。
例子
>>> from sympy.categories import Object, NamedMorphism, IdentityMorphism
>>> A = Object("A")
>>> B = Object("B")
>>> f = NamedMorphism(A, B, "f")
>>> id_A = IdentityMorphism(A)
>>> id_B = IdentityMorphism(B)
>>> f * id_A == f
True
>>> id_B * f == f
True
另见
Morphism
class sympy.categories.Category(name, objects=EmptySet, commutative_diagrams=EmptySet)
一个(抽象)范畴。
解释
一个范畴 [JoyOfCats] 是一个四元组 (\mbox{K} = (O, \hom, id, \circ)),由以下组成:
-
一个(集合论上的)类 (O),其成员称为 (K)-对象,
-
对于每对 (K)-对象 ((A, B)),一个集合 (\hom(A, B)),其成员称为从 (A) 到 (B) 的 (K)-态射,
-
对于每个 (K)-对象 (A),一个态射 (id:A\rightarrow A),称为 (A) 的 (K)-恒等态射,
-
一个复合法则 (\circ),将每个 (K)-态射 (f:A\rightarrow B) 和 (g:B\rightarrow C) 关联到一个 (K)-态射 (g\circ f:A\rightarrow C),称为 (f) 和 (g) 的复合。
复合是可结合的,(K)-恒等式在复合方面是单位元,并且集合 (\hom(A, B)) 是两两不相交的。
这个类对其对象和态射一无所知。应将(抽象)范畴的具体情况实现为从此类派生的类。
通过在构造函数中提供参数 commutative_diagrams
,可以断言某些实例的 Diagram
在 Category
中是交换的。
例子
>>> from sympy.categories import Object, NamedMorphism, Diagram, Category
>>> from sympy import FiniteSet
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g])
>>> K = Category("K", commutative_diagrams=[d])
>>> K.commutative_diagrams == FiniteSet(d)
True
另见
Diagram
property commutative_diagrams
返回已知在此范畴中为交换的图表的 FiniteSet
。
例子
>>> from sympy.categories import Object, NamedMorphism, Diagram, Category
>>> from sympy import FiniteSet
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g])
>>> K = Category("K", commutative_diagrams=[d])
>>> K.commutative_diagrams == FiniteSet(d)
True
property name
返回此范畴的名称。
例子
>>> from sympy.categories import Category
>>> K = Category("K")
>>> K.name
'K'
property objects
返回此范畴的对象类。
例子
>>> from sympy.categories import Object, Category
>>> from sympy import FiniteSet
>>> A = Object("A")
>>> B = Object("B")
>>> K = Category("K", FiniteSet(A, B))
>>> K.objects
Class({Object("A"), Object("B")})
class sympy.categories.Diagram(*args)
表示某个范畴中的图表。
解释
非正式地说,一个图表是一个类别中对象和某些它们之间的态射的集合。一个图表仍然是关于态射组合的幺半群;即,包括在图表中的所有态射的恒等态射以及所有复合态射属于图表。有关此概念的更正式方法,请参见[Pare1970]。
复合态射的组成部分也添加到图表中。此类态射默认不分配属性。
通常,一个交换图表伴随着以下类型的声明:“如果存在这样的具有这样的属性的态射,则存在这样的具有这样的属性的态射,并且图表是交换的”。为了表示这一点,图表
的一个实例包括属于相应类别的态射的集合,其具有FiniteSet
的属性的集合。
复合态射的属性集是其组成部分的属性集的交集。结论态射的定义域和值域应该是图表前提中列出的态射的定义域和值域之一。
不检查所提供的对象和态射是否属于同一类别。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy import pprint, default_sort_key
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g])
>>> premises_keys = sorted(d.premises.keys(), key=default_sort_key)
>>> pprint(premises_keys, use_unicode=False)
[g*f:A-->C, id:A-->A, id:B-->B, id:C-->C, f:A-->B, g:B-->C]
>>> pprint(d.premises, use_unicode=False)
{g*f:A-->C: EmptySet, id:A-->A: EmptySet, id:B-->B: EmptySet,id:C-->C: EmptySet, f:A-->B: EmptySet, g:B-->C: EmptySet}
>>> d = Diagram([f, g], {g * f: "unique"})
>>> pprint(d.conclusions,use_unicode=False)
{g*f:A-->C: {unique}}
参考文献
[Pare1970] B. Pareigis: 类别与函子。学术出版社,1970 年。
property conclusions
返回此图表的结论。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> from sympy.categories import IdentityMorphism, Diagram
>>> from sympy import FiniteSet
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g])
>>> IdentityMorphism(A) in d.premises.keys()
True
>>> g * f in d.premises.keys()
True
>>> d = Diagram([f, g], {g * f: "unique"})
>>> d.conclusions[g * f] == FiniteSet("unique")
True
hom(A, B)
返回对象A
和B
之间的态射集的 2 元组:一个作为前提列出的态射集,另一个作为结论列出的态射集。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy import pretty
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g], {g * f: "unique"})
>>> print(pretty(d.hom(A, C), use_unicode=False))
({g*f:A-->C}, {g*f:A-->C})
另请参阅
对象
,态射
is_subdiagram(diagram)
检查diagram
是否是self
的子图。如果图表(D')的所有前提(结论)都包含在图表(D)的前提(结论)中,则图表(D')是图表(D)的子图。对于(D')成为(D)的子图,(D')和(D)中包含的态射应具有相同的属性。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g], {g * f: "unique"})
>>> d1 = Diagram([f])
>>> d.is_subdiagram(d1)
True
>>> d1.is_subdiagram(d)
False
property objects
返回出现在此图表中的对象的FiniteSet
。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g])
>>> d.objects
{Object("A"), Object("B"), Object("C")}
property premises
返回此图表的前提。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> from sympy.categories import IdentityMorphism, Diagram
>>> from sympy import pretty
>>> A = Object("A")
>>> B = Object("B")
>>> f = NamedMorphism(A, B, "f")
>>> id_A = IdentityMorphism(A)
>>> id_B = IdentityMorphism(B)
>>> d = Diagram([f])
>>> print(pretty(d.premises, use_unicode=False))
{id:A-->A: EmptySet, id:B-->B: EmptySet, f:A-->B: EmptySet}
subdiagram_from_objects(objects)
如果objects
是self
对象的子集,则返回一个图表,其前提是所有具有objects
中的域和值域的前提,类似于结论。属性被保留。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy import FiniteSet
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g], {f: "unique", g*f: "veryunique"})
>>> d1 = d.subdiagram_from_objects(FiniteSet(A, B))
>>> d1 == Diagram([f], {f: "unique"})
True
``` ## 图表绘制本节列出了允许自动绘制图表的类别。```py
class sympy.categories.diagram_drawing.DiagramGrid(diagram, groups=None, **hints)
构造并保存图表适合网格中。
解释
本课程的任务是分析所提供的图表结构,并将其对象放置在网格上,使得当对象和态射实际绘制时,图表将“可读”,即不会有太多态射的交叉。本课程不执行任何实际绘图。尽管如此,它仍努力提供足够的元数据来绘制图表。
考虑以下简单图表。
>>> from sympy.categories import Object, NamedMorphism
>>> from sympy.categories import Diagram, DiagramGrid
>>> from sympy import pprint
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g])
将图表布局最简单的方法如下:
>>> grid = DiagramGrid(diagram)
>>> (grid.width, grid.height)
(2, 2)
>>> pprint(grid)
A BC
有时人们将图表视为由逻辑分组组成。可以通过使用 groups
关键字参数向 DiagramGrid
提供此类分组建议。
考虑以下图表:
>>> D = Object("D")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> h = NamedMorphism(D, A, "h")
>>> k = NamedMorphism(D, B, "k")
>>> diagram = Diagram([f, g, h, k])
使用通用布局排列:
>>> grid = DiagramGrid(diagram)
>>> pprint(grid)
A B DC
现在,我们可以将对象 (A) 和 (D) 分组,使它们彼此靠近:
>>> grid = DiagramGrid(diagram, groups=[[A, D], B, C])
>>> pprint(grid)
B CA D
注意其他对象的定位如何改变。
可以使用关键字参数向 DiagramGrid
的构造函数提供进一步的指示。当前支持的提示在以下段落中解释。
DiagramGrid
不会自动猜测哪种布局更适合所提供的图表。例如,考虑以下线性图表:
>>> E = Object("E")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> h = NamedMorphism(C, D, "h")
>>> i = NamedMorphism(D, E, "i")
>>> diagram = Diagram([f, g, h, i])
当使用通用布局排列时,它不会呈线性外观:
>>> grid = DiagramGrid(diagram)
>>> pprint(grid)
A BC DE
要将其布置在一条线上,请使用layout="sequential"
:
>>> grid = DiagramGrid(diagram, layout="sequential")
>>> pprint(grid)
A B C D E
有时可能需要转置结果布局。虽然这可以手动完成,但 DiagramGrid
提供了一个用于此目的的提示:
>>> grid = DiagramGrid(diagram, layout="sequential", transpose=True)
>>> pprint(grid)
ABCDE
还可以为每个组提供单独的提示。例如,请参考 tests/test_drawing.py
,查看如何布置五引理 [FiveLemma] 的不同方法。
参见
Diagram
参考
[FiveLemma]
en.wikipedia.org/wiki/Five_lemma
property height
返回此图表布局中的行数。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> from sympy.categories import Diagram, DiagramGrid
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g])
>>> grid = DiagramGrid(diagram)
>>> grid.height
2
property morphisms
返回那些足够有意义以进行绘制的态射(及其属性)。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> from sympy.categories import Diagram, DiagramGrid
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g])
>>> grid = DiagramGrid(diagram)
>>> grid.morphisms
{NamedMorphism(Object("A"), Object("B"), "f"): EmptySet,
NamedMorphism(Object("B"), Object("C"), "g"): EmptySet}
property width
返回此图表布局中的列数。
示例
>>> from sympy.categories import Object, NamedMorphism
>>> from sympy.categories import Diagram, DiagramGrid
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g])
>>> grid = DiagramGrid(diagram)
>>> grid.width
2
class sympy.categories.diagram_drawing.ArrowStringDescription(unit, curving, curving_amount, looping_start, looping_end, horizontal_direction, vertical_direction, label_position, label)
存储生成箭头的 Xy-pic 描述所需的信息。
本课程的主要目标是抽象化箭头的字符串表示,并提供生成实际 Xy-pic 字符串的功能。
unit
设置用于指定弯曲量和其他距离的单位。horizontal_direction
应该是 "r"
或 "l"
的字符串,指定相对于当前箭头的水平偏移量。vertical_direction
应该使用一系列 "d"
或 "u"
指定垂直偏移量。label_position
应该是 "^"
、"_"
或 "|"
中的一个,指定标签应位于箭头上方、下方或正好在箭头上方的位置,在此期间请注意“上方”和“下方”概念是相对于箭头方向的。label
存储了态射标签。
此方法的操作如下(忽略尚未解释的参数):
>>> from sympy.categories.diagram_drawing import ArrowStringDescription
>>> astr = ArrowStringDescription(
... unit="mm", curving=None, curving_amount=None,
... looping_start=None, looping_end=None, horizontal_direction="d",
... vertical_direction="r", label_position="_", label="f")
>>> print(str(astr))
\ar[dr]_{f}
curving
应该是 "^"
或 "_"
中的一个,用于指定箭头弯曲的方向。curving_amount
是一个数字,描述箭头弯曲的单位数目:
>>> astr = ArrowStringDescription(
... unit="mm", curving="^", curving_amount=12,
... looping_start=None, looping_end=None, horizontal_direction="d",
... vertical_direction="r", label_position="_", label="f")
>>> print(str(astr))
\ar@/¹²mm/[dr]_{f}
looping_start
和 looping_end
目前仅用于环形态射,即具有相同定义域和值域的态射。这两个属性应存储有效的 Xy-pic 方向,并相应地指定箭头向外和向内的方向:
>>> astr = ArrowStringDescription(
... unit="mm", curving=None, curving_amount=None,
... looping_start="u", looping_end="l", horizontal_direction="",
... vertical_direction="", label_position="_", label="f")
>>> print(str(astr))
\ar@(u,l)[]_{f}
label_displacement
控制箭头标签离箭头末端的距离。例如,要将箭头标签定位在箭头头部附近,请使用“>”:
>>> astr = ArrowStringDescription(
... unit="mm", curving="^", curving_amount=12,
... looping_start=None, looping_end=None, horizontal_direction="d",
... vertical_direction="r", label_position="_", label="f")
>>> astr.label_displacement = ">"
>>> print(str(astr))
\ar@/¹²mm/[dr]_>{f}
最后,arrow_style
用于指定箭头样式。例如,要获得虚线箭头,请使用“{–>}”作为箭头样式:
>>> astr = ArrowStringDescription(
... unit="mm", curving="^", curving_amount=12,
... looping_start=None, looping_end=None, horizontal_direction="d",
... vertical_direction="r", label_position="_", label="f")
>>> astr.arrow_style = "{-->}"
>>> print(str(astr))
\ar@/¹²mm/@{-->}[dr]_{f}
注意事项
XypicDiagramDrawer
将构造 ArrowStringDescription
的实例,并提供给格式化程序进一步使用。用户不应自行构造 ArrowStringDescription
的实例。
为了能够正确使用此类,请读者查看 Xy-pic 用户指南,可在 [Xypic] 上获取。
另见
XypicDiagramDrawer
参考文献
[Xypic]
xy-pic.sourceforge.net/
class sympy.categories.diagram_drawing.XypicDiagramDrawer
给定一个 Diagram
及其对应的 DiagramGrid
,生成该图示的 Xy-pic 表示。
此类中最重要的方法是 draw
。考虑以下三角形图示:
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g], {g * f: "unique"})
要绘制此图,需要使用 DiagramGrid
将其对象布局出来:
>>> grid = DiagramGrid(diagram)
最后,绘制如下:
>>> drawer = XypicDiagramDrawer()
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
C &
}
更多细节请参阅此方法的文档字符串。
要控制箭头的外观,需要使用格式化器。字典 arrow_formatters
将态射映射到格式化函数。格式化器接受一个 ArrowStringDescription
并允许修改其中公开的任何箭头属性。例如,要使所有具有 unique
属性的态射显示为虚线箭头,并在它们的名称前加上 (\exists !),应按如下方式操作:
>>> def formatter(astr):
... astr.label = r"\exists !" + astr.label
... astr.arrow_style = "{-->}"
>>> drawer.arrow_formatters["unique"] = formatter
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar@{-->}[d]_{\exists !g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
C &
}
要修改图示中所有箭头的外观,设置 default_arrow_formatter
。例如,要将所有态射标签稍微远离箭头头部,使它们看起来更加居中,可以按如下方式操作:
>>> def default_formatter(astr):
... astr.label_displacement = "(0.45)"
>>> drawer.default_arrow_formatter = default_formatter
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar@{-->}[d]_(0.45){\exists !g\circ f} \ar[r]^(0.45){f} & B \ar[ld]^(0.45){g} \\
C &
}
在一些图示中,一些态射被绘制为弯曲箭头。考虑以下图示:
>>> D = Object("D")
>>> E = Object("E")
>>> h = NamedMorphism(D, A, "h")
>>> k = NamedMorphism(D, B, "k")
>>> diagram = Diagram([f, g, h, k])
>>> grid = DiagramGrid(diagram)
>>> drawer = XypicDiagramDrawer()
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_3mm/[ll]_{h} \\
& C &
}
要控制态射的默认弯曲程度,可以使用 unit
和 default_curving_amount
属性:
>>> drawer.unit = "cm"
>>> drawer.default_curving_amount = 1
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_1cm/[ll]_{h} \\
& C &
}
在某些图示中,同一两个对象之间可能存在多个弯曲态射。要控制这些连续态射之间的弯曲程度变化,使用 default_curving_step
:
>>> drawer.default_curving_step = 1
>>> h1 = NamedMorphism(A, D, "h1")
>>> diagram = Diagram([f, g, h, k, h1])
>>> grid = DiagramGrid(diagram)
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar[r]_{f} \ar@/¹cm/[rr]^{h_{1}} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_2cm/[ll]_{h} \\
& C &
}
default_curving_step
的默认值为 4 单位。
另请参见
draw
,ArrowStringDescription
draw(diagram, grid, masked=None, diagram_format='')
返回在 grid
中布局的 diagram
的 Xy-pic 表示。
考虑以下简单的三角形图示。
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g], {g * f: "unique"})
要绘制此图示,需要使用 DiagramGrid
对其对象进行布局:
>>> grid = DiagramGrid(diagram)
最后,绘制:
>>> drawer = XypicDiagramDrawer()
>>> print(drawer.draw(diagram, grid))
\xymatrix{
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
C &
}
参数 masked
可用于在图示的表示中跳过态射:
>>> print(drawer.draw(diagram, grid, masked=[g * f]))
\xymatrix{
A \ar[r]^{f} & B \ar[ld]^{g} \\
C &
}
最后,diagram_format
参数可用于指定图示的格式字符串。例如,要增加 1 厘米的间距,可以按以下步骤进行:
>>> print(drawer.draw(diagram, grid, diagram_format="@+1cm"))
\xymatrix@+1cm{
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
C &
}
sympy.categories.diagram_drawing.xypic_draw_diagram(diagram, masked=None, diagram_format='', groups=None, **hints)
提供了一个快捷方式,结合了 DiagramGrid
和 XypicDiagramDrawer
。返回 diagram
的 Xy-pic 表示。参数 masked
是一个不绘制的态射列表。参数 diagram_format
是插入到“xymatrix”之后的格式字符串。groups
应该是一组逻辑组。hints
将直接传递给 DiagramGrid
的构造函数。
有关参数的更多信息,请参见 DiagramGrid
和 XypicDiagramDrawer.draw
的文档字符串。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy.categories import xypic_draw_diagram
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> diagram = Diagram([f, g], {g * f: "unique"})
>>> print(xypic_draw_diagram(diagram))
\xymatrix{
A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
C &
}
另请参见
XypicDiagramDrawer
, DiagramGrid
sympy.categories.diagram_drawing.preview_diagram(diagram, masked=None, diagram_format='', groups=None, output='png', viewer=None, euler=True, **hints)
结合了xypic_draw_diagram
和sympy.printing.preview
的功能。参数masked
、diagram_format
、groups
和hints
被传递给xypic_draw_diagram
,而output
,pyviewer, and ``euler
被传递给preview
。
示例
>>> from sympy.categories import Object, NamedMorphism, Diagram
>>> from sympy.categories import preview_diagram
>>> A = Object("A")
>>> B = Object("B")
>>> C = Object("C")
>>> f = NamedMorphism(A, B, "f")
>>> g = NamedMorphism(B, C, "g")
>>> d = Diagram([f, g], {g * f: "unique"})
>>> preview_diagram(d)
参见
XypicDiagramDrawer
密码学
原文链接:
docs.sympy.org/latest/modules/crypto.html
警告
此模块仅供教育目的使用。请勿将此模块中的函数用于实际的加密应用。如果希望加密真实数据,建议使用类似 cryptography 模块。
加密是隐藏消息的过程,而密码是这样做的手段。此模块包含块和流密码:
- 移位密码
- 仿射密码
- 替换密码
- 维吉尼亚密码
- Hill 密码
- 双密码
- RSA
- Kid RSA
- 线性反馈移位寄存器(用于流密码)
- ElGamal 加密
替换密码 中,根据正规系统将明文的“单位”(不一定是单个字符)替换为密文。
置换密码 是一种加密方法,通过将明文中“单位”的位置替换为明文的置换来改变单位的顺序,使用字符位置的双射函数执行加密。
单字母密码 在整个消息上使用固定的替换,而多字母密码 在消息中的不同时间使用多个替换。
sympy.crypto.crypto.AZ(s=None)
返回字符串 s
中的大写字母。如果传入多个字符串,每个字符串将被处理,并返回大写字符串列表。
示例
>>> from sympy.crypto.crypto import AZ
>>> AZ('Hello, world!')
'HELLOWORLD'
>>> AZ('Hello, world!'.split())
['HELLO', 'WORLD']
另请参阅
check_and_join
sympy.crypto.crypto.padded_key(key, symbols)
返回 symbols
的不同字符的字符串,其中 key
的字符优先出现。如果 symbols
中存在重复字符或 key
中存在不在 symbols
中的字符,则引发 ValueError。
示例
>>> from sympy.crypto.crypto import padded_key
>>> padded_key('PUPPY', 'OPQRSTUVWXY')
'PUYOQRSTVWX'
>>> padded_key('RSA', 'ARTIST')
Traceback (most recent call last):
...
ValueError: duplicate characters in symbols: T
sympy.crypto.crypto.check_and_join(phrase, symbols=None, filter=None)
连接 phrase
的字符,并且如果给定了 symbols
,则如果 phrase
中的任何字符不在 symbols
中,则引发错误。
参数:
phrase
要作为字符串返回的字符串或字符串列表。
symbols
phrase
中允许的字符的可迭代。如果
symbols
是None
,则不执行检查。
示例
>>> from sympy.crypto.crypto import check_and_join
>>> check_and_join('a phrase')
'a phrase'
>>> check_and_join('a phrase'.upper().split())
'APHRASE'
>>> check_and_join('a phrase!'.upper().split(), 'ARE', filter=True)
'ARAE'
>>> check_and_join('a phrase!'.upper().split(), 'ARE')
Traceback (most recent call last):
...
ValueError: characters in phrase but not symbols: "!HPS"
sympy.crypto.crypto.cycle_list(k, n)
返回列表 range(n)
的元素,左移 k
位(因此列表从 k
(模 n
)开始)。
示例
>>> from sympy.crypto.crypto import cycle_list
>>> cycle_list(3, 10)
[3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
sympy.crypto.crypto.encipher_shift(msg, key, symbols=None)
在明文 msg 上执行移位密码加密,并返回密文。
参数:
key :整数
秘密密钥。
msg :字符串
大写字母的明文。
返回:
字符串
大写字母的密文。
示例
>>> from sympy.crypto.crypto import encipher_shift, decipher_shift
>>> msg = "GONAVYBEATARMY"
>>> ct = encipher_shift(msg, 1); ct
'HPOBWZCFBUBSNZ'
要解密移位文本,请更改密钥的符号:
>>> encipher_shift(ct, -1)
'GONAVYBEATARMY'
还有一个方便的功能,可以使用原始密钥执行此操作:
>>> decipher_shift(ct, 1)
'GONAVYBEATARMY'
注意事项
算法:
步骤:
- 从字母表中编号字母从 0 到 N
- 从字符串
msg
计算对应整数列表L1
。- 从列表
L1
计算新列表L2
,由L1
中的每个元素加上(k mod 26)
组成。- 从列表
L2
计算对应字母的字符串ct
。
移位密码也称为凯撒密码,以朱利叶斯·凯撒命名,据苏埃托尼乌斯说,他用 3 个移位来保护军事重要消息。据报道,凯撒的侄子奥古斯都也使用了类似的密码,但右移 1 位。
另请参阅
decipher_shift
参考文献
[R151]
en.wikipedia.org/wiki/Caesar_cipher
[R152]
mathworld.wolfram.com/CaesarsMethod.html
sympy.crypto.crypto.decipher_shift(msg, key, symbols=None)
通过将msg
的字符向左移动key
给定的量来返回文本。
示例
>>> from sympy.crypto.crypto import encipher_shift, decipher_shift
>>> msg = "GONAVYBEATARMY"
>>> ct = encipher_shift(msg, 1); ct
'HPOBWZCFBUBSNZ'
要解密移位后的文本,改变密钥的符号:
>>> encipher_shift(ct, -1)
'GONAVYBEATARMY'
或使用原始密钥使用此函数:
>>> decipher_shift(ct, 1)
'GONAVYBEATARMY'
sympy.crypto.crypto.encipher_rot13(msg, symbols=None)
对给定的明文msg
执行 ROT13 加密。
说明
ROT13 是一种替换密码,将明文消息中的每个字母替换为英语字母表中距离最远的字母。
等效地,它只是一个移位密码,其移位键为 13(字母表的中间点)。
另请参阅
decipher_rot13
, encipher_shift
参考文献
[R153]
en.wikipedia.org/wiki/ROT13
sympy.crypto.crypto.decipher_rot13(msg, symbols=None)
对给定的明文msg
执行 ROT13 解密。
说明
decipher_rot13
与encipher_rot13
等效,因为decipher_shift
使用密钥 13 和encipher_shift
使用密钥 13 将返回相同的结果。尽管如此,decipher_rot13
在这里仍然明确定义以保持一致性。
示例
>>> from sympy.crypto.crypto import encipher_rot13, decipher_rot13
>>> msg = 'GONAVYBEATARMY'
>>> ciphertext = encipher_rot13(msg);ciphertext
'TBANILORNGNEZL'
>>> decipher_rot13(ciphertext)
'GONAVYBEATARMY'
>>> encipher_rot13(msg) == decipher_rot13(msg)
True
>>> msg == decipher_rot13(ciphertext)
True
sympy.crypto.crypto.encipher_affine(msg, key, symbols=None, _inverse=False)
对明文msg
执行仿射密码加密,并返回密文。
参数:
msg : str
出现在
symbols
中的字符。
a, b : int, int
一对整数,其中
gcd(a, N) = 1
(秘密密钥)。
符号
字符串的字符(默认=大写字母)。
如果没有给出符号,则将
msg
转换为大写字母,并忽略所有其他字符。
返回:
ct
字符串的字符(密文消息)
说明
加密基于映射(x \rightarrow ax+b)(mod (N)),其中N
是字母表中的字符数。解密基于映射(x \rightarrow cx+d)(mod (N)),其中(c = a^{-1})(mod (N))和(d = -a^{-1}b)(mod (N))。特别地,为了使映射可逆,我们需要(\mathrm{gcd}(a, N) = 1),如果不是这样将会引发错误。
注释
算法:
步骤:
- 将字母从 0 到 N 编号
- 从字符串
msg
计算相应整数列表L1
。- 从列表
L1
计算一个新列表L2
,替换每个元素x
为a*x + b (mod N)
。- 从列表
L2
计算字符串ct
的对应字母。
这是移位密码的简单概括,增加了需要解密两个字符才能恢复密钥的复杂性。
参见
decipher_affine
References
[R154]
en.wikipedia.org/wiki/Affine_cipher
sympy.crypto.crypto.decipher_affine(msg, key, symbols=None)
返回由映射制成的解密文本,其中映射为 (x \rightarrow ax+b) (mod (N)),其中 N
是字母表中的字符数。解密通过使用新密钥进行重新加密完成:(x \rightarrow cx+d) (mod (N)),其中 (c = a^{-1}) (mod (N)) 且 (d = -a^{-1}b) (mod (N))。
Examples
>>> from sympy.crypto.crypto import encipher_affine, decipher_affine
>>> msg = "GO NAVY BEAT ARMY"
>>> key = (3, 1)
>>> encipher_affine(msg, key)
'TROBMVENBGBALV'
>>> decipher_affine(_, key)
'GONAVYBEATARMY'
参见
encipher_affine
sympy.crypto.crypto.encipher_atbash(msg, symbols=None)
将给定的msg
加密为其 Atbash 密码文本并返回。
Explanation
Atbash 是最初用于加密希伯来字母表的代换密码。Atbash 的工作原理是将每个字母映射到其反向/对应物(即 a 映射到 z,b 映射到 y 等)。
Atbash 在功能上等同于具有 a = 25
和 b = 25
的仿射密码。
参见
decipher_atbash
sympy.crypto.crypto.decipher_atbash(msg, symbols=None)
使用 Atbash 密码解密给定的msg
并返回结果。
Explanation
decipher_atbash
在功能上等同于 encipher_atbash
。然而,它仍然作为一个单独的函数添加以保持一致性。
Examples
>>> from sympy.crypto.crypto import encipher_atbash, decipher_atbash
>>> msg = 'GONAVYBEATARMY'
>>> encipher_atbash(msg)
'TLMZEBYVZGZINB'
>>> decipher_atbash(msg)
'TLMZEBYVZGZINB'
>>> encipher_atbash(msg) == decipher_atbash(msg)
True
>>> msg == encipher_atbash(encipher_atbash(msg))
True
参见
encipher_atbash
References
[R155]
en.wikipedia.org/wiki/Atbash
sympy.crypto.crypto.encipher_substitution(msg, old, new=None)
返回通过使用old
中定义的替换替换每个出现的字符而获得的密文。如果old
是一个映射,则忽略new
并使用old
定义的替换。
Explanation
这比仿射密码更一般化,因为密钥只能通过确定每个符号的映射来恢复。尽管在实践中,一旦识别了几个符号,其他字符的映射可以很快地猜出。
Examples
>>> from sympy.crypto.crypto import encipher_substitution, AZ
>>> old = 'OEYAG'
>>> new = '034⁶'
>>> msg = AZ("go navy! beat army!")
>>> ct = encipher_substitution(msg, old, new); ct
'60N^V4B3^T^RM4'
要解密代换,反转最后两个参数:
>>> encipher_substitution(ct, new, old)
'GONAVYBEATARMY'
在 old
和 new
是 2 阶置换(表示字符置换)的特殊情况下,它们的顺序是无关紧要的:
>>> old = 'NAVY'
>>> new = 'ANYV'
>>> encipher = lambda x: encipher_substitution(x, old, new)
>>> encipher('NAVY')
'ANYV'
>>> encipher(_)
'NAVY'
代换密码通常是一种方法,按照规则系统将明文的“单元”(不一定是单个字符)替换为密文。
>>> ords = dict(zip('abc', ['\\%i' % ord(i) for i in 'abc']))
>>> print(encipher_substitution('abc', ords))
\97\98\99
References
[R156]
en.wikipedia.org/wiki/Substitution_cipher
sympy.crypto.crypto.encipher_vigenere(msg, key, symbols=None)
对明文 msg
执行维吉尼亚密码加密,并返回密文。
Examples
>>> from sympy.crypto.crypto import encipher_vigenere, AZ
>>> key = "encrypt"
>>> msg = "meet me on monday"
>>> encipher_vigenere(msg, key)
'QRGKKTHRZQEBPR'
CIA 总部的 Kryptos 雕塑第一部分使用此密码,并且还改变了字母表的顺序[R158]。以下是该雕塑部分的第一行:
>>> from sympy.crypto.crypto import decipher_vigenere, padded_key
>>> alp = padded_key('KRYPTOS', AZ())
>>> key = 'PALIMPSEST'
>>> msg = 'EMUFPHZLRFAXYUSDJKZLDKRNSHGNFIVJ'
>>> decipher_vigenere(msg, key, alp)
'BETWEENSUBTLESHADINGANDTHEABSENC'
Explanation
维吉尼亚密码因历史原因被称为布莱斯·德·维吉尼亚,一位 16 世纪的外交家和密码学家。实际上,维吉尼亚实际上发明了一种不同且更复杂的密码。所谓的维吉尼亚密码实际上是由乔瓦尼·巴蒂斯塔·贝拉索于 1553 年发明的。
这种密码在 19 世纪被使用,例如在美国内战期间。南方联盟使用黄铜密码盘实现了维吉尼亚密码(现在展览在 NSA 博物馆中的 Fort Meade)[R157]。
维吉尼亚密码是移位密码的一种推广。而移位密码每个字母的移位量是相同的(移位密码的密钥),维吉尼亚密码则是由密钥决定字母的移位量(这是发件人和收件人唯一知道的单词或短语)。
例如,如果密钥是单个字母,比如“C”,那么所谓的维吉尼亚密码实际上是一种移位密码,移位量为(2)(因为“C”是字母表中的第 2 个字母,从 0 开始计数)。如果密钥是两个字母的单词,比如“CA”,那么所谓的维吉尼亚密码将会将偶数位置的字母向右移动(2)个位置,奇数位置的字母保持不变(移位(0),因为“A”是字母表中的第 0 个字母,从 0 开始计数)。
算法:
输入:
msg
: 字符串,出现在symbols
(明文)中的字符
key
: 字符串,出现在symbols
(密钥)中的字符
symbols
: 字母串,定义字母表输出:
ct
: 字符串,字符(密文消息)步骤:
- 将字母表中的字母编号为 0,…,N
- 从字符串
key
计算对应整数列表L1
。令n1 = len(L1)
。- 从字符串
msg
计算对应整数列表L2
。令n2 = len(L2)
。- 将
L2
按大小为n1
的子列表顺序分解;最后一个子列表可能比n1
小- 对于
L2
的每个子列表L
,计算一个新列表C
,给定为C[i] = L[i] + L1[i] (mod N)
到子列表中的第i
个元素,对于每个i
。- 将这些列表
C
通过串联成一个长度为n2
的新列表。- 从新列表计算对应字母串
ct
。
一旦知道密钥长度为(n)个字符,可以对密文的每第(n)个字母应用频率分析来确定明文。这种方法称为Kasiski 检查(尽管它最初由巴贝奇发现)。如果密钥与消息一样长,并由随机选择的字符组成——一次性密码本——则理论上消息是不可破解的。
维吉尼亚密码实际上发现的是一种“自动密钥”密码,如下所述。
算法:
输入:
key
: 字母串,(密钥)
msg
: 字母串,(明文消息)输出:
ct
: 大写字母串,(密文消息)步骤:
- 将字母表中的字母编号为 0,…,N
- 从字符串
msg
计算一个与之对应的整数列表L2
。设n2 = len(L2)
。- 让
n1
是密钥的长度。将明文消息的前n2 - n1
个字符附加到字符串key
,计算这个长度为n2
的字符串(也是长度为n2
的字符串),得到一个与第一步中字母号对应的整数列表L1
。- 计算一个新列表
C
,其定义为C[i] = L1[i] + L2[i] (mod N)
。- 从新列表
C
计算一个与新整数对应的字母字符串ct
。
为了解密自动密钥的密文,使用密钥来解密前 n1
个字符,然后这些字符成为解密下一个 n1
个字符的密钥,依此类推。
>>> m = AZ('go navy, beat army! yes you can'); m
'GONAVYBEATARMYYESYOUCAN'
>>> key = AZ('gold bug'); n1 = len(key); n2 = len(m)
>>> auto_key = key + m[:n2 - n1]; auto_key
'GOLDBUGGONAVYBEATARMYYE'
>>> ct = encipher_vigenere(m, auto_key); ct
'MCYDWSHKOGAMKZCELYFGAYR'
>>> n1 = len(key)
>>> pt = []
>>> while ct:
... part, ct = ct[:n1], ct[n1:]
... pt.append(decipher_vigenere(part, key))
... key = pt[-1]
...
>>> ''.join(pt) == m
True
References
[R157] (1,2)
en.wikipedia.org/wiki/Vigenere_cipher
[R158] (1,2)
web.archive.org/web/20071116100808/https://filebox.vt.edu/users/batman/kryptos.html
(短链接:goo.gl/ijr22d
)
sympy.crypto.crypto.decipher_vigenere(msg, key, symbols=None)
使用维吉尼亚密码解密。
Examples
>>> from sympy.crypto.crypto import decipher_vigenere
>>> key = "encrypt"
>>> ct = "QRGK kt HRZQE BPR"
>>> decipher_vigenere(ct, key)
'MEETMEONMONDAY'
sympy.crypto.crypto.encipher_hill(msg, key, symbols=None, pad='Q')
返回 msg
的 Hill 密码加密。
Parameters:
msg
n
个大写字母的明文消息。
key
一个 (k \times k) 可逆矩阵 (K),其所有条目都在 (Z_{26}) 中(或者所用符号的任何数量中)。
pad
字符(默认为“Q”)用于使文本长度为
k
的倍数。
Returns:
ct
大写字母的密文。
Explanation
Hill 密码 [R159],由莱斯特·希尔在 1920 年代发明 [R160],是第一个可以同时处理超过三个符号的多图密码。以下讨论假设对矩阵有基本的了解。
首先,每个字母都被编码为从 0 开始的一个数字。假设您的消息 msg
由 n
个大写字母组成,没有空格。这可以看作是 n
元组 M
,其元素属于 Z_{26}
(如果字母是英语字母)。在 Hill 密码中,一个密钥是一个 (k \times k) 矩阵 K
,其所有条目都在 Z_{26}
中,并且矩阵 K
可逆(即,线性变换 K: Z_{N}^k \rightarrow Z_{N}^k
是一对一的)。
Notes
ALGORITHM:
STEPS:
- 将字母从 0 到 N 编号
- 从字符串
msg
计算一个与之对应的整数列表L
。设n = len(L)
。- 将列表
L
分解为t = ceiling(n/k)
个大小为k
的子列表L_1
, …,L_t
(最后一个列表被“填充”以确保其大小为k
)。- 计算新列表
C_1
, …,C_t
,其定义为C[i] = K*L_i
(所有的算术都在模N
下进行),对每个i
。- 将它们连接成一个列表
C = C_1 + ... + C_t
。- 从
C
计算一个长度为k*t
的字符串ct
,其对应于相应的字母。
See also
decipher_hill
References
[R159] (1,2)
en.wikipedia.org/wiki/Hill_cipher
[R160] (1,2)
Lester S. Hill,《代数字母表中的密码学》,《美国数学月刊》第 36 卷,1929 年 6 月至 7 月,第 306-312 页。
sympy.crypto.crypto.decipher_hill(msg, key, symbols=None)
解密与加密相同,但使用密钥矩阵的逆。
示例
>>> from sympy.crypto.crypto import encipher_hill, decipher_hill
>>> from sympy import Matrix
>>> key = Matrix([[1, 2], [3, 5]])
>>> encipher_hill("meet me on monday", key)
'UEQDUEODOCTCWQ'
>>> decipher_hill(_, key)
'MEETMEONMONDAY'
当明文长度(去除无效字符)不是密钥维度的倍数时,加密和解密文本的末尾将出现额外字符。为了解密文本,这些字符必须包含在待解密的文本中。接下来,密钥的维度为 4,但文本缺少 4 的倍数,因此将添加两个字符。
>>> key = Matrix([[1, 1, 1, 2], [0, 1, 1, 0],
... [2, 2, 3, 4], [1, 1, 0, 1]])
>>> msg = "ST"
>>> encipher_hill(msg, key)
'HJEB'
>>> decipher_hill(_, key)
'STQQ'
>>> encipher_hill(msg, key, pad="Z")
'ISPK'
>>> decipher_hill(_, key)
'STZZ'
如果忽略了密文的最后两个字符,则无论如何都会恢复错误的明文:
>>> decipher_hill("HD", key)
'ORMV'
>>> decipher_hill("IS", key)
'UIKY'
参见
encipher_hill
sympy.crypto.crypto.encipher_bifid(msg, key, symbols=None)
对明文 msg
进行 Bifid 密码加密,并返回密文。
这是使用 (n \times n) Polybius 方阵的 Bifid 密码的版本。
参数:
消息
明文字符串。
密钥
密钥的简短字符串。
忽略重复字符,然后用
symbols
中不在短密钥中的字符填充。
符号
(n \times n) 字符定义字母表。
(默认为 string.printable)
返回:
密文
使用 Bifid5 密码的无空格密文。
参见
decipher_bifid
, encipher_bifid5
, encipher_bifid6
参考文献
[R161]
en.wikipedia.org/wiki/Bifid_cipher
sympy.crypto.crypto.decipher_bifid(msg, key, symbols=None)
对密文 msg
进行 Bifid 密码解密,并返回明文。
这是使用 (n \times n) Polybius 方阵的 Bifid 密码的版本。
参数:
消息
密文字符串。
密钥
密钥的简短字符串。
忽略重复字符,然后用不在短密钥中的符号填充。
符号
(n \times n) 字符定义字母表。
(默认=string.printable,一个 (10 \times 10) 矩阵)
返回:
解密
解密后的文本。
示例
>>> from sympy.crypto.crypto import (
... encipher_bifid, decipher_bifid, AZ)
使用 bifid5 字母表进行加密:
>>> alp = AZ().replace('J', '')
>>> ct = AZ("meet me on monday!")
>>> key = AZ("gold bug")
>>> encipher_bifid(ct, key, alp)
'IEILHHFSTSFQYE'
在输入文本或密文时,忽略空格,以便根据需要格式化。重新输入上述密文,每行放置 4 个字符并用额外的 J 填充,不会导致解密问题:
>>> decipher_bifid('''
... IEILH
... HFSTS
... FQYEJ''', key, alp)
'MEETMEONMONDAY'
当没有给出字母表时,将使用所有 100 个可打印字符:
>>> key = ''
>>> encipher_bifid('hello world!', key)
'bmtwmg-bIo*w'
>>> decipher_bifid(_, key)
'hello world!'
如果更改密钥,则会获得不同的加密结果:
>>> key = 'gold bug'
>>> encipher_bifid('hello world!', 'gold_bug')
'hg2sfuei7t}w'
如果用于解密消息的密钥不完全匹配,原始文本将无法完美获取:
>>> decipher_bifid(_, 'gold pug')
'heldo~wor6d!'
sympy.crypto.crypto.bifid5_square(key=None)
5x5 Polybius 方阵。
生成 (5 \times 5) Bifid 密码的 Polybius 方阵。
例子
>>> from sympy.crypto.crypto import bifid5_square
>>> bifid5_square("gold bug")
Matrix([
[G, O, L, D, B],
[U, A, C, E, F],
[H, I, K, M, N],
[P, Q, R, S, T],
[V, W, X, Y, Z]])
sympy.crypto.crypto.encipher_bifid5(msg, key)
执行 Bifid 密码加密以获得明文 msg
的结果,并返回密文。
参数:
消息:str
明文字符串。
转换为大写并过滤掉除了所有字母之外的任何东西。
密钥
用于密钥的短字符串;忽略非字母字符、J 和重复字符,如果长度少于 25 个字符,则用字母表中的其他字母(按字母顺序)填充。
返回:
ct
密文(全大写,无空格)。
解释
这是使用 (5 \times 5) Polybius 方阵的 Bifid 密码的版本。字母“J”被忽略,因此在加密之前必须用其他东西替换(传统上是“I”)。
算法:(5x5 情况)
步骤:
- 创建与
key
相关的 (5 \times 5) Polybius 方阵S
的方式如下:
- 从左到右,从上到下,将密钥的字母放入一个 (5 \times 5) 矩阵中,
- 如果密钥少于 25 个字母,添加字母表中不在密钥中的字母,直到填满 (5 \times 5) 方阵。
- 创建
msg
中字母在 Polybius 方阵中的坐标对的列表P
。- 设
L1
为P
的所有第一个坐标的列表(L1
的长度为n
),L2
为P
的所有第二个坐标的列表(因此L2
的长度也为n
)。- 设
L
为L1
和L2
的连接(长度为L = 2*n
),但连续的数字成对出现(L[2*i], L[2*i + 1])
。您可以将L
视为长度为n
的对的列表。- 设
C
为形如S[i, j]
的所有字母的列表,对于所有(i, j)
属于L
。作为字符串,这是msg
的密文。
例子
>>> from sympy.crypto.crypto import (
... encipher_bifid5, decipher_bifid5)
“J” 将被省略,除非用其他内容替换:
>>> round_trip = lambda m, k: \
... decipher_bifid5(encipher_bifid5(m, k), k)
>>> key = 'a'
>>> msg = "JOSIE"
>>> round_trip(msg, key)
'OSIE'
>>> round_trip(msg.replace("J", "I"), key)
'IOSIE'
>>> j = "QIQ"
>>> round_trip(msg.replace("J", j), key).replace(j, "J")
'JOSIE'
注释
Bifid 密码是由 Felix Delastelle 大约在 1901 年发明的。它是一种 分数替代 密码,其中字母被较小字母表中的一对符号替换。该密码使用填充了字母顺序的 (5 \times 5) 方阵,除了将“J”替换为“I”之外(这是所谓的 Polybius 方阵;如果添加回“J”并且在通常的 26 个字母表后附加数字 0、1、…、9,则有一个 (6 \times 6) 的类似物)。根据 Helen Gaines 的书籍 Cryptanalysis,这种类型的密码曾被第一次世界大战期间的德国军队使用。
另请参阅
decipher_bifid5
, encipher_bifid
sympy.crypto.crypto.decipher_bifid5(msg, key)
返回 msg
的 Bifid 密码解密。
参数:
消息
密文字符串。
密钥
用于密钥的短字符串;忽略重复字符,并且如果长度小于 25 个字符,则用字母表中排除“J”的其他字母填充。
返回:
明文
Plaintext from Bifid5 cipher (all caps, no spaces).
解释
这是使用(5 \times 5) Polybius 方阵的 Bifid 密码版本;除非使用长度为 25 的key
,否则字母“J”将被忽略。
示例
>>> from sympy.crypto.crypto import encipher_bifid5, decipher_bifid5
>>> key = "gold bug"
>>> encipher_bifid5('meet me on friday', key)
'IEILEHFSTSFXEE'
>>> encipher_bifid5('meet me on monday', key)
'IEILHHFSTSFQYE'
>>> decipher_bifid5(_, key)
'MEETMEONMONDAY'
sympy.crypto.crypto.encipher_bifid6(msg, key)
对明文msg
执行 Bifid 密码加密,并返回密文。
这是使用(6 \times 6) Polybius 方阵的 Bifid 密码版本。
参数:
msg
明文字符串(允许数字)。
key
用于密钥的简短字符串(允许数字)。
如果
key
长度小于 36 个字符,方阵将填充字母 A 到 Z 和数字 0 到 9。
返回:
密文
来自 Bifid 密码的密文(全大写,无空格)。
另请参阅
decipher_bifid6
,encipher_bifid
sympy.crypto.crypto.decipher_bifid6(msg, key)
对密文msg
执行 Bifid 密码解密,并返回明文。
这是使用(6 \times 6) Polybius 方阵的 Bifid 密码版本。
参数:
msg
密文字符串(允许数字);转换为大写
key
用于密钥的简短字符串(允许数字)。
如果
key
长度小于 36 个字符,方阵将填充字母 A 到 Z 和数字 0 到 9。所有字母均转换为大写。
返回:
明文
来自 Bifid 密码的明文(全大写,无空格)。
示例
>>> from sympy.crypto.crypto import encipher_bifid6, decipher_bifid6
>>> key = "gold bug"
>>> encipher_bifid6('meet me on monday at 8am', key)
'KFKLJJHF5MMMKTFRGPL'
>>> decipher_bifid6(_, key)
'MEETMEONMONDAYAT8AM'
sympy.crypto.crypto.bifid6_square(key=None)
6x6 Polybius 方阵。
生成(6 \times 6) Bifid 密码的 Polybius 方阵。假设符号的字母表是“A”,…,“Z”,“0”,…,“9”。
示例
>>> from sympy.crypto.crypto import bifid6_square
>>> key = "gold bug"
>>> bifid6_square(key)
Matrix([
[G, O, L, D, B, U],
[A, C, E, F, H, I],
[J, K, M, N, P, Q],
[R, S, T, V, W, X],
[Y, Z, 0, 1, 2, 3],
[4, 5, 6, 7, 8, 9]])
sympy.crypto.crypto.rsa_public_key(*args, **kwargs)
返回 RSA 的公钥对,((n, e))
参数:
args:自然数
如果指定为(p, q, e),其中(p)和(q)是不同的素数,(e)是 RSA 的所需公共指数,则(n = p q),(e)将针对欧拉函数(\phi(n))或卡米歇尔函数(\lambda(n))进行验证,以确保(\gcd(e, \phi(n)) = 1)或(\gcd(e, \lambda(n)) = 1)。
如果指定为(p_1, p_2, \dots, p_n, e),其中(p_1, p_2, \dots, p_n)被指定为素数,(e)被指定为 RSA 的所需公共指数,则能够形成多素数 RSA,这是流行的双素数 RSA 的更广义形式。
还可以通过将参数指定为(p, e)来形成单素数 RSA,这可以被看作是多素数 RSA 的特例。
此外,还可以通过指定两个或更多对相同的素数来形成多功率 RSA。但是,与双不同素数 RSA 或多素数 RSA 不同,完全剩余系统((\mathbb{Z}n))中的每个数字都将无法解密,因为映射(\mathbb{Z} \rightarrow \mathbb{Z}_{n})不是双射的。(只有在(e = 1)或更一般地时除外,
[e \in \left { 1 + k \lambda(n) \mid k \in \mathbb{Z} \land k \geq 0 \right }]
当 RSA 归结为恒等式时。)但是,RSA 仍然可以解密在减少的剩余系统((\mathbb{Z}n{\times}))中的数字,因为映射(\mathbb{Z}_{n} \rightarrow \mathbb{Z}^{\times})仍然可以是双射。
如果将非素数整数传递给参数(p_1, p_2, \dots, p_n),则该特定数将被素数因子化,并且它将成为其规范形式中的多素数 RSA 或多功率 RSA,具体取决于乘积是否等于其基数或不等。(p_1 p_2 \dots p_n = \text{rad}(p_1 p_2 \dots p_n))
totient:布尔值,可选
如果是'欧拉',它使用欧拉的欧拉函数 (\phi(n)),即
sympy.functions.combinatorial.numbers.totient()
在 SymPy 中。如果是'卡迈克尔',它使用卡迈克尔的欧拉函数 (\lambda(n)),即
sympy.functions.combinatorial.numbers.reduced_totient()
在 SymPy 中。与私钥生成不同,这是公钥生成的一个微不足道的关键字,因为(\gcd(e, \phi(n)) = 1 \iff \gcd(e, \lambda(n)) = 1)。
index:非负整数,可选
返回指定在(0, 1, 2, \dots)处的 RSA 公钥的任意解。此参数需要与
totient='Carmichael'
一起指定。与描述
rsa_private_key()
中的index
参数文档的 RSA 私钥的非唯一性类似,RSA 公钥也不唯一,并且有无限多个可以以相同方式运行的 RSA 公共指数。对于任何给定的 RSA 公共指数(e),都可以有另一个 RSA 公共指数(e + k \lambda(n)),其中(k)是整数,(\lambda)是卡迈克尔的欧拉函数。
然而,仅考虑正数情况,可以有 RSA 公共指数(e_0)的主要解,在(0 < e_0 < \lambda(n)),并且所有其他解可以以(e_0 + k \lambda(n))的形式规范化。
index
指定(k)表示法,以产生 RSA 公钥可能具有的任何可能值。计算任意 RSA 公钥的示例:
>>> from sympy.crypto.crypto import rsa_public_key>>> rsa_public_key(61, 53, 17, totient='Carmichael', index=0)(3233, 17)>>> rsa_public_key(61, 53, 17, totient='Carmichael', index=1)(3233, 797)>>> rsa_public_key(61, 53, 17, totient='Carmichael', index=2)(3233, 1577)
multipower:布尔值,可选
在 RSA 规范中找到的任何非不同素数对将限制密码系统的域,如
args
参数说明中所述。SymPy RSA 密钥生成器在将其分发为多功率 RSA 之前可能会发出警告,但是,如果将
True
传递给此关键字,可以禁用警告。
返回:
(n, e):整数,整数
(n)是给定作为参数的任意数量的素数的乘积。
(e)与欧拉函数(\phi(n))是相对素数(互质)的。
错误
如果给出的参数少于两个或 (e) 与模数不互质,则返回。
示例
>>> from sympy.crypto.crypto import rsa_public_key
两素数 RSA 的公钥:
>>> p, q, e = 3, 5, 7
>>> rsa_public_key(p, q, e)
(15, 7)
>>> rsa_public_key(p, q, 30)
False
多素数 RSA 的公钥:
>>> primes = [2, 3, 5, 7, 11, 13]
>>> e = 7
>>> args = primes + [e]
>>> rsa_public_key(*args)
(30030, 7)
注意
尽管 RSA 可以推广到任意模数 (n),但使用两个大素数已经成为最流行的规范,因为两个大素数的乘积通常是相对于 (n) 的位数最难分解的。
但可能需要进一步了解每个素数分解算法的时间复杂度来验证该说法。
另请参阅
rsa_private_key
, encipher_rsa
, decipher_rsa
参考文献
[R162]
en.wikipedia.org/wiki/RSA_%28cryptosystem%29
[R163]
cacr.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
[R164]
link.springer.com/content/pdf/10.1007/BFb0055738.pdf
[R165]
www.itiis.org/digital-library/manuscript/1381
sympy.crypto.crypto.rsa_private_key(*args, **kwargs)
返回 RSA 私钥 对,((n, d))
参数:
args : 自然数
该关键字与
rsa_public_key()
中的args
相同。
totient : 布尔值,可选
如果是
'Euler'
,则使用欧拉的欧拉函数约定 (\phi(n)),即 SymPy 中的sympy.functions.combinatorial.numbers.totient()
。如果是
'Carmichael'
,则使用 Carmichael 的欧拉函数约定 (\lambda(n)),即 SymPy 中的sympy.functions.combinatorial.numbers.reduced_totient()
。对于私钥生成,可能会有一些输出差异,如下面的示例。
使用欧拉的示例:
>>> from sympy.crypto.crypto import rsa_private_key>>> rsa_private_key(61, 53, 17, totient='Euler')(3233, 2753)
使用 Carmichael 的欧拉函数的示例:
>>> from sympy.crypto.crypto import rsa_private_key>>> rsa_private_key(61, 53, 17, totient='Carmichael')(3233, 413)
index : 非负整数,可选
返回指定索引 (0, 1, 2, \dots) 处的 RSA 私钥的任意解。此参数需与
totient='Carmichael'
一起指定。RSA 私钥指数是 (e d \mod \lambda(n) = 1) 的非唯一解,可以用 (d + k \lambda(n)) 的形式表示,其中 (d) 是另一个已计算的私钥指数,(\lambda) 是 Carmichael 的欧拉函数,(k) 是任意整数。
然而,仅考虑正面情况,RSA 私钥指数(d_0)在(0 < d_0 < \lambda(n))中可能有一个主要解,所有其他解可以被规范化为(d_0 + k \lambda(n))的形式。
index
指定了(k)符号来产生 RSA 私钥可能有的任何可能值。计算任意 RSA 私钥的示例:
>>> from sympy.crypto.crypto import rsa_private_key>>> rsa_private_key(61, 53, 17, totient='Carmichael', index=0)(3233, 413)>>> rsa_private_key(61, 53, 17, totient='Carmichael', index=1)(3233, 1193)>>> rsa_private_key(61, 53, 17, totient='Carmichael', index=2)(3233, 1973)
多幂:布尔值,可选
关键字与
rsa_public_key()
中的multipower
相同。
返回:
(n, d):整数,整数
(n)是作为参数给出的任意数量质数的乘积。
(d)是给定指数(e)的模(\phi(n))的逆,(\phi)是欧拉函数。
False
如果给出少于两个参数,或者(e)与模数的欧拉函数不互质,则返回。
示例
>>> from sympy.crypto.crypto import rsa_private_key
双质数 RSA 的私钥:
>>> p, q, e = 3, 5, 7
>>> rsa_private_key(p, q, e)
(15, 7)
>>> rsa_private_key(p, q, 30)
False
多质数 RSA 的私钥:
>>> primes = [2, 3, 5, 7, 11, 13]
>>> e = 7
>>> args = primes + [e]
>>> rsa_private_key(*args)
(30030, 823)
另请参阅
rsa_public_key
,encipher_rsa
,decipher_rsa
的关键字。
参考文献
[R166]
en.wikipedia.org/wiki/RSA_%28cryptosystem%29
[R167]
cacr.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
[R168]
link.springer.com/content/pdf/10.1007/BFb0055738.pdf
[R169]
www.itiis.org/digital-library/manuscript/1381
sympy.crypto.crypto.encipher_rsa(i, key, factors=None)
使用 RSA 加密明文。
参数:
i:整数
待加密的明文。
密钥:(n, e),其中 n、e 为整数
(n)是密钥的模数,(e)是密钥的指数。加密计算为(i^e \bmod n)。
密钥可以是公钥或私钥,但通过公钥加密的消息只能由私钥解密,反之亦然,因为 RSA 是一种非对称加密系统。
因子:互质整数列表
这与
decipher_rsa()
中的关键字factors
相同。
注意
一些规范可能使 RSA 不具有密码学上的意义。
例如,(0),(1)在进行任意次幂后始终保持不变,因此应避免使用。
此外,如果(i^e < n),则(i)可能很容易通过取(e)次根来找到。
并且,将指数指定为(1)或更一般的形式,如(1 + k \lambda(n)),其中(k)是非负整数,(\lambda)是卡迈克尔欧拉函数,RSA 变成一个恒等映射。
示例
>>> from sympy.crypto.crypto import encipher_rsa
>>> from sympy.crypto.crypto import rsa_public_key, rsa_private_key
公钥加密:
>>> p, q, e = 3, 5, 7
>>> puk = rsa_public_key(p, q, e)
>>> msg = 12
>>> encipher_rsa(msg, puk)
3
私钥加密:
>>> p, q, e = 3, 5, 7
>>> prk = rsa_private_key(p, q, e)
>>> msg = 12
>>> encipher_rsa(msg, prk)
3
使用中国剩余定理加密:
>>> encipher_rsa(msg, prk, factors=[p, q])
3
sympy.crypto.crypto.decipher_rsa(i, key, factors=None)
使用 RSA 解密密文。
参数:
i : 整数
要解密的密文。
key : (n, d),其中 n, d 是整数
(n) 是密钥的模数,(d) 是密钥的指数。解密通过 (i^d \bmod n) 计算。
密钥可以是公钥或私钥,然而,用公钥加密的消息只能用私钥解密,反之亦然,因为 RSA 是一种非对称加密系统。
factors : 互质整数列表
由于 RSA 密钥生成中创建的模数 (n) 由任意的素数因子组成 (n = {p_1}{k_1}{p_2}\dots{p_n}^{k_n}),其中 (p_1, p_2, \dots, p_n) 是不同的素数,(k_1, k_2, \dots, k_n) 是正整数,可以使用中国剩余定理从分段的模操作计算 (i^d \bmod n)。
[i^d \bmod {p_1}^{k_1}, i^d \bmod {p_2}^{k_2}, \dots, i^d \bmod {p_n}^{k_n}]
或者如下
[i^d \bmod {p_1}{k_1}{p_2}, i^d \bmod {p_3}^{k_3}, \dots , i^d \bmod {p_n}^{k_n}]
只要每个模数之间没有共同的除数。
用于生成 RSA 密钥对的原始素数可以是一个不错的选择。
注意使用此方法的速度优势仅适用于非常大的情况(如 2048 位 RSA 密钥),因为使用
sympy.ntheory.modular.crt()
的纯 Python 实现的开销可能超过了理论上的速度优势。
注释
在encipher_rsa()
的文档的Notes
部分查看。
示例
>>> from sympy.crypto.crypto import decipher_rsa, encipher_rsa
>>> from sympy.crypto.crypto import rsa_public_key, rsa_private_key
公钥加密和解密:
>>> p, q, e = 3, 5, 7
>>> prk = rsa_private_key(p, q, e)
>>> puk = rsa_public_key(p, q, e)
>>> msg = 12
>>> new_msg = encipher_rsa(msg, prk)
>>> new_msg
3
>>> decipher_rsa(new_msg, puk)
12
私钥加密和解密:
>>> p, q, e = 3, 5, 7
>>> prk = rsa_private_key(p, q, e)
>>> puk = rsa_public_key(p, q, e)
>>> msg = 12
>>> new_msg = encipher_rsa(msg, puk)
>>> new_msg
3
>>> decipher_rsa(new_msg, prk)
12
使用中国剩余定理进行解密:
>>> decipher_rsa(new_msg, prk, factors=[p, q])
12
参见
encipher_rsa
sympy.crypto.crypto.kid_rsa_public_key(a, b, A, B)
Kid RSA 是一种版本的 RSA,适合用来教授小学生,因为它不涉及指数运算。
解释
Alice 想要和 Bob 交流。Bob 生成密钥如下。密钥生成:
-
随机选择正整数 (a, b, A, B)。
-
计算 (M = a b - 1), (e = A M + a), (d = B M + b), (n = (e d - 1)//M)。
-
公钥 是 ((n, e))。Bob 将这些发送给 Alice。
-
私钥 是 ((n, d)), 这是 Bob 保密的。
加密:如果 (p) 是明文消息,则密文是 (c = p e \pmod n)。
解密:如果 (c) 是密文消息,则明文是 (p = c d \pmod n)。
示例
>>> from sympy.crypto.crypto import kid_rsa_public_key
>>> a, b, A, B = 3, 4, 5, 6
>>> kid_rsa_public_key(a, b, A, B)
(369, 58)
sympy.crypto.crypto.kid_rsa_private_key(a, b, A, B)
计算 (M = a b - 1), (e = A M + a), (d = B M + b), (n = (e d - 1) / M)。私钥 是 (d), 这是 Bob 保密的。
示例
>>> from sympy.crypto.crypto import kid_rsa_private_key
>>> a, b, A, B = 3, 4, 5, 6
>>> kid_rsa_private_key(a, b, A, B)
(369, 70)
sympy.crypto.crypto.encipher_kid_rsa(msg, key)
这里 msg
是明文,key
是公钥。
示例
>>> from sympy.crypto.crypto import (
... encipher_kid_rsa, kid_rsa_public_key)
>>> msg = 200
>>> a, b, A, B = 3, 4, 5, 6
>>> key = kid_rsa_public_key(a, b, A, B)
>>> encipher_kid_rsa(msg, key)
161
sympy.crypto.crypto.decipher_kid_rsa(msg, key)
这里 msg
是明文,key
是私钥。
示例
>>> from sympy.crypto.crypto import (
... kid_rsa_public_key, kid_rsa_private_key,
... decipher_kid_rsa, encipher_kid_rsa)
>>> a, b, A, B = 3, 4, 5, 6
>>> d = kid_rsa_private_key(a, b, A, B)
>>> msg = 200
>>> pub = kid_rsa_public_key(a, b, A, B)
>>> pri = kid_rsa_private_key(a, b, A, B)
>>> ct = encipher_kid_rsa(msg, pub)
>>> decipher_kid_rsa(ct, pri)
200
sympy.crypto.crypto.encode_morse(msg, sep='|', mapping=None)
将明文编码为普通的摩斯密码,字母之间用 sep
分隔,单词之间用双 sep
分隔。
示例
>>> from sympy.crypto.crypto import encode_morse
>>> msg = 'ATTACK RIGHT FLANK'
>>> encode_morse(msg)
'.-|-|-|.-|-.-.|-.-||.-.|..|--.|....|-||..-.|.-..|.-|-.|-.-'
参考
[R170]
en.wikipedia.org/wiki/Morse_code
sympy.crypto.crypto.decode_morse(msg, sep='|', mapping=None)
解码摩斯电码,字母用 sep
(默认为‘|’)分隔,单词用 (word_sep)(默认为‘||’)分隔成纯文本。
示例
>>> from sympy.crypto.crypto import decode_morse
>>> mc = '--|---|...-|.||.|.-|...|-'
>>> decode_morse(mc)
'MOVE EAST'
参考文献
[R171]
en.wikipedia.org/wiki/Morse_code
sympy.crypto.crypto.lfsr_sequence(key, fill, n)
此函数创建一个 LFSR 序列。
参数:
密钥:list
有限域元素列表,([c_0, c_1, \ldots, c_k])。
填充:list
LFSR 序列的初始项列表,([x_0, x_1, \ldots, x_k])。
n
函数返回的序列项数。
返回:
L
由 (x_{n+1} = c_k x_n + \ldots + c_0 x_{n-k}) 定义的 LFSR 序列,对于 (n \leq k)。
备注
S. Golomb [G171] 给出了一个数字序列 (a = {a_n}_{n=1}^\infty), (a_n \in {0,1}),应显示为“随机”的三个统计属性列表。定义 (a) 的自相关为
[C(k) = C(k,a) = \lim_{N\rightarrow \infty} {1\over N}\sum_{n=1}^N (-1)^{a_n + a_{n+k}}.]
如果 (a) 周期为 (P),则此情况下简化为
[C(k) = {1\over P}\sum_{n=1}^P (-1)^{a_n + a_{n+k}}.]
假设 (a) 是周期为 (P) 的周期性序列。
-
平衡:
[\left|\sum_{n=1}P(-1)\right| \leq 1.]
-
低自相关:
[\begin{split}C(k) = \left{ \begin{array}{cc} 1,& k = 0,\ \epsilon, & k \ne 0. \end{array} \right.\end{split}]
(对于满足这两个属性的序列,已知必须满足 (\epsilon = -1/P)。)
-
比例运行特性:在每个周期内,一半的运行长度为 (1),四分之一的运行长度为 (2),等等。此外,(1) 的运行数与 (0) 的运行数相同。
示例
>>> from sympy.crypto.crypto import lfsr_sequence
>>> from sympy.polys.domains import FF
>>> F = FF(2)
>>> fill = [F(1), F(1), F(0), F(1)]
>>> key = [F(1), F(0), F(0), F(1)]
>>> lfsr_sequence(key, fill, 10)
[1 mod 2, 1 mod 2, 0 mod 2, 1 mod 2, 0 mod 2,
1 mod 2, 1 mod 2, 0 mod 2, 0 mod 2, 1 mod 2]
参考文献
[G171] (1,2)
Solomon Golomb, Shift register sequences, Aegean Park Press, Laguna Hills, Ca, 1967
sympy.crypto.crypto.lfsr_autocorrelation(L, P, k)
此函数计算 LFSR 自相关函数。
参数:
L
(GF(2)) 元素的周期序列。 L 必须比 P 长。
P
L 的周期。
k:int
一个整数 (k) ((0 < k < P))。
返回:
自相关
LFSR L 的自相关的第 k 个值。
示例
>>> from sympy.crypto.crypto import (
... lfsr_sequence, lfsr_autocorrelation)
>>> from sympy.polys.domains import FF
>>> F = FF(2)
>>> fill = [F(1), F(1), F(0), F(1)]
>>> key = [F(1), F(0), F(0), F(1)]
>>> s = lfsr_sequence(key, fill, 20)
>>> lfsr_autocorrelation(s, 15, 7)
-1/15
>>> lfsr_autocorrelation(s, 15, 0)
1
sympy.crypto.crypto.lfsr_connection_polynomial(s)
此函数计算 LFSR 连接多项式。
参数:
s
一个偶数长度的元素序列,其条目在有限域内。
返回:
C(x)
生成 s 的最小 LFSR 的连接多项式。
此处实现了 J. L. Massey 文章第三部分的算法 [M172]。
示例
>>> from sympy.crypto.crypto import (
... lfsr_sequence, lfsr_connection_polynomial)
>>> from sympy.polys.domains import FF
>>> F = FF(2)
>>> fill = [F(1), F(1), F(0), F(1)]
>>> key = [F(1), F(0), F(0), F(1)]
>>> s = lfsr_sequence(key, fill, 20)
>>> lfsr_connection_polynomial(s)
x**4 + x + 1
>>> fill = [F(1), F(0), F(0), F(1)]
>>> key = [F(1), F(1), F(0), F(1)]
>>> s = lfsr_sequence(key, fill, 20)
>>> lfsr_connection_polynomial(s)
x**3 + 1
>>> fill = [F(1), F(0), F(1)]
>>> key = [F(1), F(1), F(0)]
>>> s = lfsr_sequence(key, fill, 20)
>>> lfsr_connection_polynomial(s)
x**3 + x**2 + 1
>>> fill = [F(1), F(0), F(1)]
>>> key = [F(1), F(0), F(1)]
>>> s = lfsr_sequence(key, fill, 20)
>>> lfsr_connection_polynomial(s)
x**3 + x + 1
参考文献
[M172] (1,2)
James L. Massey, “Shift-Register Synthesis and BCH Decoding.” IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127, Jan 1969.
sympy.crypto.crypto.elgamal_public_key(key)
以公钥返回三个数字元组。
参数:
密钥:(p, r, e)
由
elgamal_private_key
生成的元组。
返回:
元组:(p, r, e)
(e = r**d \bmod p)
(d) 是私钥中的随机数。
示例
>>> from sympy.crypto.crypto import elgamal_public_key
>>> elgamal_public_key((1031, 14, 636))
(1031, 14, 212)
sympy.crypto.crypto.elgamal_private_key(digit=10, seed=None)
以私钥返回三个数字元组。
参数:
数字:int
密钥的最小二进制位数。
返回:
元组:(p, r, d)
p = 素数。
r = 原根。
d = 随机数。
说明
Elgamal 加密基于称为离散对数问题(DLP)的数学问题。例如,
(a^{b} \equiv c \pmod p)
一般来说,如果已知 a
和 b
,则很容易计算 ct
。如果 b
是未知的,则很难使用 a
和 ct
来获得 b
。
注释
为了测试目的,可以设置 seed
参数以控制此过程的输出。参见 sympy.core.random._randrange。
示例
>>> from sympy.crypto.crypto import elgamal_private_key
>>> from sympy.ntheory import is_primitive_root, isprime
>>> a, b, _ = elgamal_private_key()
>>> isprime(a)
True
>>> is_primitive_root(b, a)
True
sympy.crypto.crypto.encipher_elgamal(i, key, seed=None)
使用公钥加密消息。
参数:
msg
编码消息的整数。
key
公钥。
返回:
tuple : (c1, c2)
将加密成两个数字。
说明
i
是以整数表示的明文消息。 key
是公钥(p,r,e)。为了加密消息,生成范围为 range(2, p)
中的随机数 a
,并返回加密后的消息作为 (c_{1}) 和 (c_{2}),其中:
(c_{1} \equiv r^{a} \pmod p)
(c_{2} \equiv m e^{a} \pmod p)
注释
为了测试目的,可以设置 seed
参数以控制此过程的输出。参见 sympy.core.random._randrange。
示例
>>> from sympy.crypto.crypto import encipher_elgamal, elgamal_private_key, elgamal_public_key
>>> pri = elgamal_private_key(5, seed=[3]); pri
(37, 2, 3)
>>> pub = elgamal_public_key(pri); pub
(37, 2, 8)
>>> msg = 36
>>> encipher_elgamal(msg, pub, seed=[3])
(8, 6)
sympy.crypto.crypto.decipher_elgamal(msg, key)
使用私钥解密消息。
(msg = (c_{1}, c_{2}))
(key = (p, r, d))
根据扩展欧几里得定理,(u c_{1}^{d} + p n = 1)
(u \equiv 1/{{c_{1}}^d} \pmod p)
(u c_{2} \equiv \frac{1}{c_{1}^d} c_{2} \equiv \frac{1}{r^{ad}} c_{2} \pmod p)
(\frac{1}{r^{ad}} m e^a \equiv \frac{1}{r^{ad}} m {r^{d a}} \equiv m \pmod p)
示例
>>> from sympy.crypto.crypto import decipher_elgamal
>>> from sympy.crypto.crypto import encipher_elgamal
>>> from sympy.crypto.crypto import elgamal_private_key
>>> from sympy.crypto.crypto import elgamal_public_key
>>> pri = elgamal_private_key(5, seed=[3])
>>> pub = elgamal_public_key(pri); pub
(37, 2, 8)
>>> msg = 17
>>> decipher_elgamal(encipher_elgamal(msg, pub), pri) == msg
True
sympy.crypto.crypto.dh_public_key(key)
返回三个数字元组作为公钥。
这是 Alice 发送给 Bob 的元组。
参数:
key : (p, g, a)
由
dh_private_key
生成的元组。
返回:
tuple : int, int, int
一个元组 ((p, g, g^a \mod p)),其中给定参数为
p
,g
和a
。
示例
>>> from sympy.crypto.crypto import dh_private_key, dh_public_key
>>> p, g, a = dh_private_key();
>>> _p, _g, x = dh_public_key((p, g, a))
>>> p == _p and g == _g
True
>>> x == pow(g, a, p)
True
sympy.crypto.crypto.dh_private_key(digit=10, seed=None)
返回三个整数元组作为私钥。
参数:
digit
钥匙所需的最小二进制位数。
返回:
tuple : (p, g, a)
p = 素数。
g = p 的原根。
a = 从 2 到 p-1 的随机数。
说明
Diffie-Hellman 密钥交换基于称为离散对数问题的数学问题(参见 ElGamal)。
Diffie-Hellman 密钥交换分为以下步骤:
-
Alice 和 Bob 同意一个基础,由素数
p
和称为g
的原根组成。 -
Alice 选择一个数字
a
,Bob 选择一个数字b
,其中a
和b
是在范围 ([2, p)) 中的随机数。这些是他们的私钥。 -
Alice 公开发送 (g^{a} \pmod p) 给 Bob,而 Bob 向 Alice 发送 (g^{b} \pmod p)。
-
他们都将接收到的值提升到他们秘密选择的数字(
a
或b
)上,现在两者都有g^{ab} \pmod p
作为他们的共享密钥。
注释
为了测试目的,可以设置 seed
参数以控制此过程的输出。参见 sympy.core.random._randrange。
示例
>>> from sympy.crypto.crypto import dh_private_key
>>> from sympy.ntheory import isprime, is_primitive_root
>>> p, g, _ = dh_private_key()
>>> isprime(p)
True
>>> is_primitive_root(g, p)
True
>>> p, g, _ = dh_private_key(5)
>>> isprime(p)
True
>>> is_primitive_root(g, p)
True
sympy.crypto.crypto.dh_shared_key(key, b)
返回一个整数,即共享密钥。
这是 Bob 和 Alice 可以使用彼此收到的公钥及其私钥计算的内容。
参数:
key : (p, g, x)
由
dh_public_key
生成的元组 ((p, g, x))。
b
在范围(2)到(p - 1)内的随机数(由第二个密钥交换成员(Bob)选择)。
返回:
int
共享密钥。
示例
>>> from sympy.crypto.crypto import (
... dh_private_key, dh_public_key, dh_shared_key)
>>> prk = dh_private_key();
>>> p, g, x = dh_public_key(prk);
>>> sk = dh_shared_key((p, g, x), 1000)
>>> sk == pow(x, 1000, p)
True
sympy.crypto.crypto.gm_public_key(p, q, a=None, seed=None)
计算p
和q
的公钥。请注意,在 Goldwasser-Micali 加密中,公钥是随机选择的。
参数:
p, q, a:int, int, int
初始化变量。
返回:
tuple:(a, N)
如果
a
不为None
,则a
是输入的a
,否则是一些与p
和q
互质的随机整数。
N
是p
和q
的乘积。
sympy.crypto.crypto.gm_private_key(p, q, a=None)
检查p
和q
是否可以用作 Goldwasser-Micali 加密的私钥。该方法大致如下运行。
参数:
p, q, a
初始化变量。
返回:
tuple:(p, q)
输入值
p
和q
。
引发:
ValueError
如果
p
和q
不是不同的奇素数。
解释
-
选择两个大素数(p)和(q)。
-
称它们的乘积为(N)。
-
给定一个整数消息(i),将(i)用其比特表示写成(b_0, \dots, b_n)。
-
对于每个(k),
如果(b_k = 0):
让(a_k)是一个随机平方数(二次剩余),模(p q),使得
jacobi_symbol(a, p*q) = 1
如果(b_k = 1):
让(a_k)是一个随机的非平方数(非二次剩余),模(p q),使得
jacobi_symbol(a, p*q) = 1
返回(\left[a_1, a_2, \dots\right])
(b_k)可以通过检查(a_k)是否是余数来恢复。并且从(b_k)中,消息可以被重建。
思路是,虽然jacobi_symbol(a, p*q)
可以很容易计算(当等于(-1)时,表明(a)在模(p q)下不是二次剩余),但二次剩余性模一个复合数的难度在不知其因数分解的情况下很难计算。
此外,大约一半与(p q)互质的数具有jacobi_symbol()
等于(1)。在这些数中,大约一半是剩余,大约一半不是。这最大化了代码的熵。
sympy.crypto.crypto.encipher_gm(i, key, seed=None)
使用公钥‘key’加密整数‘i’注意,gm 使用随机加密。
参数:
i:int
要加密的消息。
key:(a, N)
公钥。
返回:
list:int 列表
随机化的加密消息。
sympy.crypto.crypto.decipher_gm(message, key)
使用公钥‘key’解密消息‘message’。
参数:
message:int 列表
随机化的加密消息。
key:(p, q)
私钥。
返回:
int
加密后的消息。
sympy.crypto.crypto.encipher_railfence(message, rails)
在明文上执行栅栏加密并返回密文
参数:
message:string,要加密的消息。
rails:int,栏的数量。
返回:
加密后的字符串消息。
示例
>>> from sympy.crypto.crypto import encipher_railfence
>>> message = "hello world"
>>> encipher_railfence(message,3)
'horel ollwd'
参考文献
[R174]
zh.wikipedia.org/wiki/铁栏加密
sympy.crypto.crypto.decipher_railfence(ciphertext, rails)
使用给定的栏解密消息
参数:
message:string,要加密的消息。
rails:int,栏的数量。
返回:
解密后的字符串消息。
示例
>>> from sympy.crypto.crypto import decipher_railfence
>>> decipher_railfence("horel ollwd",3)
'hello world'