范畴论
引言
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 B
C
有时人们将图表视为由逻辑分组组成。可以通过使用 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 D
C
现在,我们可以将对象 (A) 和 (D) 分组,使它们彼此靠近:
>>> grid = DiagramGrid(diagram, groups=[[A, D], B, C])
>>> pprint(grid)
B C
A 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 B
C D
E
要将其布置在一条线上,请使用layout="sequential"
:
>>> grid = DiagramGrid(diagram, layout="sequential")
>>> pprint(grid)
A B C D E
有时可能需要转置结果布局。虽然这可以手动完成,但 DiagramGrid
提供了一个用于此目的的提示:
>>> grid = DiagramGrid(diagram, layout="sequential", transpose=True)
>>> pprint(grid)
A
B
C
D
E
还可以为每个组提供单独的提示。例如,请参考 tests/test_drawing.py
,查看如何布置五引理 [FiveLemma] 的不同方法。
参见
Diagram
参考
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]
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
密码学
警告
此模块仅供教育目的使用。请勿将此模块中的函数用于实际的加密应用。如果希望加密真实数据,建议使用类似 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]
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]
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}{n} \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}{\times} \rightarrow \mathbb{Z}{n}^{\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}{k_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}{k_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){a_n}\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]
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'
是在范围 ([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]
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'