1 计算幂模 m m m
如何计算:
5
100
000
000
000
000
(
m
o
d
12
830
603
)
5^{100\ 000\ 000\ 000\ 000}\ (mod\ 12\ 830\ 603)
5100 000 000 000 000 (mod 12 830 603)
呢?这么大的数字显然是不能硬算的,如果
12830603
12830603
12830603是素数,我们可以使用费马小定理简化计算,即便不是素数,也可以使用欧拉公式。但即便简化了计算,剩余的计算量依旧大的离谱:
5
6
546
640
(
m
o
d
12
830
603
)
5^{6\ 546\ 640}\ (mod\ 12\ 830\ 603)
56 546 640 (mod 12 830 603)
进行这样的大数计算是很实用的,它可以用来做加密,而且产生的密码使用目前最先进的破译技术也不能破译。用来计算 a k ( m o d m ) a^k\ (mod\ m) ak (mod m)的一个巧妙想法叫做逐次平方法。在叙述一般方法之前,先用一个例子体现它的思想。
7 327 ( m o d 853 ) 7^{327}\ (mod\ 853) 7327 (mod 853)
第一步创建表格,给出 7 , 7 2 , 7 4 , 7 8 , 7 16 , ⋯ ( m o d 853 ) 7,7^2,7^4,7^8,7^{16},\cdots (mod\ 853) 7,72,74,78,716,⋯(mod 853)的值。注意,要得到表中的每个项,我们仅仅需要平方前面的数。由于在平方前总是模 853 853 853进行简化,所以从未使数大于 85 3 2 853^2 8532,下面是 7 2 k ( m o d 853 ) 7^{2^k}\ (mod\ 853) 72k (mod 853)的列表
7 1 ≡ 7 ≡ 7 ( m o d 853 ) 7 2 ≡ ( 7 1 ) 2 ≡ 7 2 ≡ 49 ≡ 49 ( m o d 853 ) 7 4 ≡ ( 7 2 ) 2 ≡ 4 9 2 ≡ 2401 ≡ 695 ( m o d 853 ) 7 8 ≡ ( 7 4 ) 2 ≡ 69 5 2 ≡ 483025 ≡ 227 ( m o d 853 ) 7 16 ≡ ( 7 8 ) 2 ≡ 22 7 2 ≡ 51529 ≡ 349 ( m o d 853 ) 7 32 ≡ ( 7 16 ) 2 ≡ 34 9 2 ≡ 121801 ≡ 675 ( m o d 853 ) 7 64 ≡ ( 7 32 ) 2 ≡ 67 5 2 ≡ 455625 ≡ 123 ( m o d 853 ) 7 128 ≡ ( 7 64 ) 2 ≡ 12 3 2 ≡ 15129 ≡ 628 ( m o d 853 ) 7 256 ≡ ( 7 128 ) 2 ≡ 62 8 2 ≡ 394384 ≡ 298 ( m o d 853 ) \begin{aligned} &7^1\equiv7\equiv7\ (mod\ 853)\\ &7^2\equiv(7^1)^2\equiv7^2\equiv 49\equiv 49\ (mod\ 853)\\ &7^4\equiv(7^2)^2\equiv49^2\equiv 2401\equiv 695\ (mod\ 853)\\ &7^8\equiv(7^4)^2\equiv695^2\equiv 483025\equiv 227\ (mod\ 853)\\ &7^{16}\equiv(7^8)^2\equiv227^2\equiv 51529\equiv 349\ (mod\ 853)\\ &7^{32}\equiv(7^{16})^2\equiv349^2\equiv 121801\equiv 675\ (mod\ 853)\\ &7^{64}\equiv(7^{32})^2\equiv675^2\equiv 455625\equiv 123\ (mod\ 853)\\ &7^{128}\equiv(7^{64})^2\equiv123^2\equiv 15129\equiv 628\ (mod\ 853)\\ &7^{256}\equiv(7^{128})^2\equiv628^2\equiv 394384\equiv 298\ (mod\ 853) \end{aligned} 71≡7≡7 (mod 853)72≡(71)2≡72≡49≡49 (mod 853)74≡(72)2≡492≡2401≡695 (mod 853)78≡(74)2≡6952≡483025≡227 (mod 853)716≡(78)2≡2272≡51529≡349 (mod 853)732≡(716)2≡3492≡121801≡675 (mod 853)764≡(732)2≡6752≡455625≡123 (mod 853)7128≡(764)2≡1232≡15129≡628 (mod 853)7256≡(7128)2≡6282≡394384≡298 (mod 853)
下一步,将指数 327 327 327表成 2 2 2的幂次和:
327
=
256
+
71
=
256
+
64
+
7
=
256
+
64
+
4
+
3
=
256
+
64
+
4
+
2
+
1
327 = 256+71=256+64+7=256+64+4+3=256+64+4+2+1
327=256+71=256+64+7=256+64+4+3=256+64+4+2+1
现在使用
327
327
327的二进制展开式计算
7
327
=
7
256
+
64
+
4
+
2
+
1
=
298
∗
123
∗
695
∗
49
∗
7
(
m
o
d
853
)
7^{327} = 7^{256+64+4+2+1} = 298*123*695*49*7\ (mod\ 853)
7327=7256+64+4+2+1=298∗123∗695∗49∗7 (mod 853)
要完成
7
327
(
m
o
d
853
)
7^{327}\ (mod\ 853)
7327 (mod 853)的计算,我们只需要程这
5
5
5个数,然后用模
853
853
853进行简化。如果所有
5
5
5个数的乘积对我们来说尝试太大,则可以乘前两个数简化,再与第三个数相乘简化,以这种方式,我们就不需要对任何大于
85
3
2
853^2
8532的数进行计算,因此:
298
∗
123
∗
695
∗
49
∗
7
(
m
o
d
853
)
=
828
∗
695
∗
49
∗
7
(
m
o
d
853
)
=
538
∗
49
∗
7
(
m
o
d
853
)
=
772
∗
7
(
m
o
d
853
)
≡
286
(
m
o
d
853
)
\begin{aligned} 298*123*695*49*7\ (mod\ 853) &= 828*695*49*7\ (mod\ 853) \\ &= 538*49*7\ (mod\ 853)\\ &= 772*7\ (mod\ 853)\equiv\ 286\ (mod\ 853) \end{aligned}
298∗123∗695∗49∗7 (mod 853)=828∗695∗49∗7 (mod 853)=538∗49∗7 (mod 853)=772∗7 (mod 853)≡ 286 (mod 853)
2 逐次平方法
算法(逐次平方计算 a k ( m o d m ) a^k\ (mod\ m) ak (mod m)):用下述步骤计算 a k ( m o d m ) a^k\ (mod\ m) ak (mod m)的值:
- 将
k
k
k表成
2
2
2的幂次和:
k = u 0 + u 1 ∗ 2 + u 2 ∗ 2 2 + u 3 ∗ 2 3 + ⋯ + u r ∗ 2 r k = u_0 + u_1*2+u_2*2^2+u_3*2^3+\cdots +u_r*2^r k=u0+u1∗2+u2∗22+u3∗23+⋯+ur∗2r
其中 u i u_i ui为 0 0 0或 1 1 1 - 使用逐次平方法制作模
m
m
m的
a
a
a的幂次表
a 1 ≡ A 0 ( m o d m ) a 2 ≡ ( a 1 ) 2 ≡ A 0 2 ≡ A 1 ( m o d m ) a 4 ≡ ( a 2 ) 2 ≡ A 1 2 ≡ A 2 ( m o d m ) a 8 ≡ ( a 4 ) 2 ≡ A 2 2 ≡ A 3 ( m o d m ) ⋮ a 2 r ≡ ( a 2 r − 1 ) 2 ≡ A r − 1 2 ≡ A r ( m o d m ) \begin{aligned} &a^1\equiv A_0\ (mod\ m)\\ &a^2\equiv(a^1)^2\equiv A_0^2\equiv A_1\ (mod\ m)\\ &a^4\equiv(a^2)^2\equiv A_1^2\equiv A_2\ (mod\ m)\\ &a^8\equiv(a^4)^2\equiv A_2^2\equiv A_3\ (mod\ m)\\ &\vdots\\ &a^{2^r}\equiv(a^{2^{r-1}})^2\equiv A_{r-1}^2\equiv A_r\ (mod\ m)\\ \end{aligned} a1≡A0 (mod m)a2≡(a1)2≡A02≡A1 (mod m)a4≡(a2)2≡A12≡A2 (mod m)a8≡(a4)2≡A22≡A3 (mod m)⋮a2r≡(a2r−1)2≡Ar−12≡Ar (mod m)
注意要计算表的每一行,仅需要取前一行最末的数,平方它然后用模 m m m简化。也注意到表有 r + 1 r+1 r+1行,其中 r r r是第一步中 k k k的二进制展开式中 2 2 2的最高指数。 - 乘积:
A 0 u 0 ∗ A 1 u 1 ∗ A 2 u 2 ∗ ⋯ ∗ A r u r ( m o d m ) A_0^{u_0}*A_1^{u_1}*A_2^{u_2}*\cdots *A_r^{u_r}\ (mod\ m) A0u0∗A1u1∗A2u2∗⋯∗Arur (mod m)
同余于 a k ( m o d m ) a^k\ (mod\ m) ak (mod m)。注意到所有 u i u_i ui是 0 , 1 0,1 0,1。因此这个数实际上是 u i = 1 u_i=1 ui=1的那些 A i A_i Ai的乘积。
3 算法实现
下述算法是进行逐次平方法的更有效方法。
算法流程:
- 置 b = 1 b=1 b=1
- 当
k
≥
1
k\ge 1
k≥1时,循环:
1.如果 k k k是奇数则 b = a ∗ b ( m o d m ) b = a*b\ (mod\ m) b=a∗b (mod m)
2.置 a = a 2 ( m o d m ) a=a^2\ (mod\ m) a=a2 (mod m)
3.置 k = k / 2 k = k/2 k=k/2(如果 k k k是奇数,转下) - 结束循环
- 转到 b b b的值
下面来解释一下该算法,首先需要知道一个取模运算的运算法则,即:
(
a
∗
b
)
%
p
=
(
a
%
p
∗
b
%
p
)
%
p
(a*b)\%p = (a\%p * b\%p)\% p
(a∗b)%p=(a%p∗b%p)%p
对于一个大数次幂,可以通过降幂的方法来进行加速计算,降幂方法就是指数除以
2
2
2,底数开平方,例如
a
11
a^{11}
a11,我们可以通过以下步骤来计算:
a
11
=
(
a
2
)
5
∗
a
=
(
a
2
)
4
∗
a
2
∗
a
=
(
a
4
)
2
∗
a
2
∗
a
=
(
a
8
)
1
∗
a
2
∗
a
\begin{aligned} a^{11} &= (a^2)^5*a\\ &=(a^2)^4*a^2*a\\ &=(a^4)^2 * a^2*a\\ &=(a^8)^1 * a^2*a \end{aligned}
a11=(a2)5∗a=(a2)4∗a2∗a=(a4)2∗a2∗a=(a8)1∗a2∗a
这个过程我们发现,
a
11
a^{11}
a11的结果就是在降幂过程中,幂为奇数时的各个底数的乘积,即
a
11
,
(
a
2
)
5
,
(
a
8
)
1
a^{11},(a^2)^5,(a^8)^1
a11,(a2)5,(a8)1的底数
a
,
a
2
,
a
8
a,a^2,a^8
a,a2,a8。
def zcpf(a, k, m):
b = 1
a %= m
while k >= 1:
if k % 2 == 1:
b = (a * b) % m
a = a**2 % m
k //= 2
return b
print(zcpf(7, 327, 853))
输出:
286
4 参考资料
《数论概论》第四版 P74-P77