解决思路
当一个幂运算很大,而模为整型数时,通常的做法(先求幂再取模),结果很大可能就是数溢出,无法表示这样的大数,导致运算失败。 可以先试着将数分割成几个部分,然后一个部分一部分的求取模数,从而实现目的。
引用基础数学公式
可以用数学公式解决的问题,千万别去计算能力暴力解决。因为可以用公式解决的问题,时间空间的复杂度很可能直接就是O(1)
公式1证明:
数学公式:
(
a
∗
b
)
m
o
d
C
=
(
(
a
m
o
d
C
)
∗
b
)
m
o
d
C
;
(
a
,
b
,
C
皆是是整数
)
(a * b ) \quad mod \quad C = (( a \quad mod \quad C) * b) \quad mod \quad C; (a, b, C 皆是是整数)
(a∗b)modC=((amodC)∗b)modC;(a,b,C皆是是整数)
设:a=m+k, m 的值为 C 的 n 倍数(n>=0,且为整数), k < C
则:
(
a
∗
b
)
m
o
d
C
=
(
(
m
+
k
)
∗
b
)
m
o
d
C
=
(
m
b
+
k
b
)
m
o
d
C
=
(
C
n
b
+
k
b
)
m
o
d
C
=
k
b
m
o
d
C
\begin{align*} (a * b ) \quad mod \quad C&= ( ( m + k ) * b ) \quad mod \quad C \\ &= ( mb + kb ) \quad mod \quad C \\ &= ( Cnb + kb ) \quad mod \quad C \\ &= kb \quad mod \quad C \end{align*}
(a∗b)modC=((m+k)∗b)modC=(mb+kb)modC=(Cnb+kb)modC=kbmodC
因为:
k
=
a
m
o
d
C
k = a \quad mod \quad C
k=amodC
所以:
(
a
∗
b
)
m
o
d
C
=
(
(
a
m
o
d
C
)
∗
b
)
m
o
d
C
(a * b ) \quad mod \quad C = ( ( a \quad mod \quad C ) * b ) \quad mod \quad C
(a∗b)modC=((amodC)∗b)modC
公式2证明:
数学公式
a
∗
b
m
o
d
C
=
(
a
m
o
d
C
)
∗
(
b
m
o
d
C
)
m
o
d
C
(
a
,
b
,
C
皆是是整数
)
a * b\quad mod \quad C = ( a\quad mod \quad C) * ( b\quad mod \quad C) \quad mod\quad C (a, b, C 皆是是整数)
a∗bmodC=(amodC)∗(bmodC)modC(a,b,C皆是是整数)
设:
a
=
m
+
k
1
,
b
=
n
+
k
2
,
m
,
n
的值为
C
的
x
倍数(
x
>
=
0
,且为整数)
,
k
1
,
k
2
<
C
a=m+k_1, b=n+k_2, m,n的值为 C 的 x 倍数(x>=0,且为整数), k_1,k_2 < C
a=m+k1,b=n+k2,m,n的值为C的x倍数(x>=0,且为整数),k1,k2<C
则:
a
∗
b
=
(
m
+
k
1
)
∗
(
n
+
k
2
)
=
m
n
+
m
k
2
+
k
1
n
+
k
1
k
2
由于
m
和
n
都是
C
的
x
倍得到:
=
C
x
1
C
x
2
+
C
x
1
k
2
+
k
1
C
x
2
+
k
1
k
2
m
o
d
C
=
k
1
k
2
m
o
d
C
k
1
=
a
m
o
d
C
k
2
=
b
m
o
d
C
可得:
a
∗
b
m
o
d
C
=
(
a
m
o
d
C
)
∗
(
b
m
o
d
C
)
m
o
d
C
\begin{align*} a*b&=(m+k_1)*(n+k_2)\\ &=mn+mk_2+k_1n+k_1k_2 \\ 由于&m和n都是C的x倍 得到:\\ &=Cx_1Cx_2+Cx_1k_2+k_1Cx_2+k_1k_2 \quad mod \quad C\\ &=k_1k_2\quad mod \quad C\\ k_1&=a \quad mod \quad C\\ k_2&=b \quad mod \quad C\\ 可得:\\ a * b\quad mod \quad C &= ( a\quad mod \quad C) * ( b\quad mod \quad C) \quad mod\quad C \end{align*}
a∗b由于k1k2可得:a∗bmodC=(m+k1)∗(n+k2)=mn+mk2+k1n+k1k2m和n都是C的x倍得到:=Cx1Cx2+Cx1k2+k1Cx2+k1k2modC=k1k2modC=amodC=bmodC=(amodC)∗(bmodC)modC
公式3证明:
数学公式:
a
b
m
o
d
C
=
(
a
m
o
d
C
)
b
m
o
d
C
(
a
,
b
,
C
皆是是整数
)
a ^ b\quad mod \quad C = ( a\quad mod \quad C) ^ b \quad mod\quad C (a, b, C 皆是是整数)
abmodC=(amodC)bmodC(a,b,C皆是是整数)
设: a = m + k , m 的值为 C 的 n 倍数( b , n > = 0 ,且为整数) , k < C a=m+k, m 的值为 C 的 n 倍数(b, n>=0,且为整数), k < C a=m+k,m的值为C的n倍数(b,n>=0,且为整数),k<C
则根据牛顿二项式展开得到:
a
b
m
o
d
C
=
(
m
+
k
)
b
m
o
d
C
=
(
∑
i
=
0
b
d
i
m
b
−
i
k
i
)
m
o
d
C
=
(
d
0
m
b
+
d
1
m
b
−
1
k
+
d
2
m
b
−
2
k
2
+
d
3
m
b
−
3
k
3
+
⋯
+
d
b
−
1
m
k
b
−
1
+
d
b
k
b
)
m
o
d
C
d
0
,
d
1
,
d
2
,
d
3
⋯
d
b
>
0
且都是整数(牛顿二项式系数)
\begin{align*} a ^ b \quad mod \quad C &= ( m + k ) ^ b \quad mod \quad C \\ &=\bigg( \displaystyle\sum_{i=0}^b d_im^{b-i}k^i \bigg) \quad mod \quad C\\ &= ( d_0m^b + d_1m^{b-1}k + d_2m^{b-2}k^2 + d_3m^{b-3}k^3 + \cdots + d_{b-1}mk^{b-1} + d_bk^b ) \quad mod \quad C \\ \\ d_0,d_1,d_2,d_3 \cdots d_b >0 &且都是整数(牛顿二项式系数) \\ \end{align*}
abmodCd0,d1,d2,d3⋯db>0=(m+k)bmodC=(i=0∑bdimb−iki)modC=(d0mb+d1mb−1k+d2mb−2k2+d3mb−3k3+⋯+db−1mkb−1+dbkb)modC且都是整数(牛顿二项式系数)
因为
d
0
,
d
b
=
1
d_0,d_b=1
d0,db=1 所以得到:
(
m
b
+
d
1
m
b
−
1
k
+
d
2
m
b
−
2
k
2
+
d
3
m
b
−
3
k
3
+
⋯
+
d
b
−
1
m
k
b
−
1
+
k
b
)
( m^b + d_1m^{b-1}k + d_2m^{b-2}k^2 + d_3m^{b-3}k^3 + \cdots + d_{b-1}mk^{b-1} +k^b ) \\
(mb+d1mb−1k+d2mb−2k2+d3mb−3k3+⋯+db−1mkb−1+kb)
前 b 项全换成C的倍数形式,得到
(
(
C
n
)
b
+
d
1
(
C
n
)
b
−
1
k
+
d
2
(
C
n
)
b
−
2
k
2
+
d
3
(
C
n
)
b
−
3
k
3
+
⋯
+
d
b
−
1
(
C
n
)
k
b
−
1
+
k
b
)
m
o
d
C
=
k
b
m
o
d
C
k
=
a
m
o
d
C
\begin{align*} & ( (Cn)^b + d_1(Cn)^{b-1}k + d_2(Cn)^{b-2}k^2 + d_3(Cn)^{b-3}k^3 + \cdots + d_{b-1}(Cn)k^{b-1} +k^b ) \quad mod \quad C \\ & = k^b \quad mod \quad C \\ k&= a \quad mod \quad C \\ \end{align*}
k((Cn)b+d1(Cn)b−1k+d2(Cn)b−2k2+d3(Cn)b−3k3+⋯+db−1(Cn)kb−1+kb)modC=kbmodC=amodC
所有的C的倍数全被排除了,所以:
a
b
m
o
d
C
=
(
a
m
o
d
C
)
b
m
o
d
C
\begin{align*} a^b \quad mod \quad C = ( a \quad mod \quad C ) ^ b \quad mod \quad C \end{align*}
abmodC=(amodC)bmodC
猜想,但我无法证明
任意一个质数a, 任何一个小于a且大于0的整数d,
则有:
d
a
m
o
d
a
=
d
d^a \quad mod \quad a = d
damoda=d
大数分解
如: a b a^b ab mod C 求解 (a, b, C 皆是是整数)
- 首先把 b 转化成二进制
如: b 0 , b 1 , b 2 , b 3 , b 4 ⋯ b 31 b_0, b_1, b_2, b_3, b_4 \cdots b_{31} b0,b1,b2,b3,b4⋯b31
即: b = b 0 ∗ 2 31 + b 1 ∗ 2 30 + b 2 ∗ 2 29 + ⋯ + b 31 ∗ 2 0 ; b = b_0*2^{31} + b_1*2^{30} + b_2*2^{29} + \cdots + b_{31}*2^0; b=b0∗231+b1∗230+b2∗229+⋯+b31∗20;
也就是把
a b = a ( b 0 ∗ 2 31 + b 1 ∗ 2 30 + ⋯ + b 31 ) = a ( b 0 ∗ 2 31 ) ∗ a ( b 1 ∗ 2 30 ) ∗ a ( b 2 ∗ 2 29 ) . . . . . ∗ a ( b 31 ∗ 2 0 ) ; \begin{align*} a^b &= a ^{ (b_0*2^{31} + b_1*2^{30}+ \cdots + b_{31}) } \\ &= a^{(b_0*2^{31})} * a^{(b_1*2^{30})} * a^{(b_2*2^{29})} ..... * a^{(b_{31}*2^0)}; \end{align*} ab=a(b0∗231+b1∗230+⋯+b31)=a(b0∗231)∗a(b1∗230)∗a(b2∗229).....∗a(b31∗20);
例如: 8 25 8^{25} 825 mod 5 ; 25转化成二进制为11001,即:
8 25 = 8 ( 1 ∗ 2 16 ) ∗ 8 ( 1 ∗ 2 8 ) ∗ 8 ( 0 ∗ 2 4 ) ∗ 8 ( 0 ∗ 2 2 ) ∗ 8 ( 1 ∗ 2 0 ) = 8 16 ∗ 8 8 ∗ 8 0 ∗ 8 0 ∗ 8 1 = 8 1 ∗ 8 0 ∗ 8 0 ∗ 8 8 ∗ 8 16 \begin{align*} 8^{25} &= 8^{(1*2^{16})} * 8^{(1*2^8)} * 8^{(0*2^4)} * 8^{(0*2^2)} * 8^{(1*2^0)} \\ &= 8^{16} * 8^8 * 8^0 * 8^0 * 8^1 \\ &= 8^1 * 8^0 * 8^0 * 8^8 * 8^{16} \end{align*} 825=8(1∗216)∗8(1∗28)∗8(0∗24)∗8(0∗22)∗8(1∗20)=816∗88∗80∗80∗81=81∗80∗80∗88∗816
所以:
8
25
m
o
d
5
=
(
8
1
∗
8
0
∗
8
0
∗
8
8
∗
8
16
)
m
o
d
5
\begin{align*} 8^{25} \quad mod \quad5 &= (8^1 * 8^0 * 8^0 * 8^8 * 8^{16}) \quad mod \quad5 \end{align*}
825mod5=(81∗80∗80∗88∗816)mod5
程序逻辑实现:
以指数为25时11001为例:
循环 | 权重 | 赋值 | 取余 | 二进制值 | 说明 | |
---|---|---|---|---|---|---|
1 | 1 | W= a % c | W=a | result = (result * W) % c; | 1 | 该位值为1,所以将当前权重值保存到result中 |
2 | 2 | W= (W* W) % c | W= a 2 a^2 a2 | * | 0 | 为了计算高阶的值,继续计算权重 该位值为0,result不需要这个值 |
3 | 4 | W= (W* W) % c | W= a 4 a^4 a4 | * | 0 | 为了计算高阶的值,继续计算权重 该位值为0,result不需要这个值 |
4 | 8 | W= (W * W) % c | W= a 8 a^8 a8 | result = (result * W) % c | 1 | 为了计算高阶的值,继续计算权重 该位值为1,所以将当前权重值保存到result中 |
5 | 16 | W= (W * W) % c | W= a 16 a^{16} a16 | result = (result * W) % c | 1 | 为了计算高阶的值,继续计算权重 该位值为1,所以将当前权重值保存到result中 |
指数=1+2+8+16,相对a来说,就是result= a 1 ∗ a 2 ∗ a 8 ∗ a 16 m o d c a^1*a^2*a^8*a^{16} \quad mod \quad c a1∗a2∗a8∗a16modc
根据上述的公式可以推导出:
设:
r
e
s
u
l
t
=
8
1
m
o
d
5
=
8
m
o
d
5
=
3
8
2
m
o
d
5
等于
r
e
s
u
l
t
=
r
e
s
u
l
t
∗
r
e
s
u
l
t
m
o
d
5
;
此时
r
e
s
u
l
t
就等于
8
2
m
o
d
5
=
4
8
4
m
o
d
5
等于
r
e
s
u
l
t
=
r
e
s
u
l
t
∗
r
e
s
u
l
t
m
o
d
5
;
此时
r
e
s
u
l
t
就等于
8
4
m
o
d
5
=
1
8
8
m
o
d
5
等于
r
e
s
u
l
t
=
r
e
s
u
l
t
∗
r
e
s
u
l
t
m
o
d
5
;
此时
r
e
s
u
l
t
就等于
8
8
m
o
d
5
=
1
8
16
m
o
d
5
等于
r
e
s
u
l
t
=
r
e
s
u
l
t
∗
r
e
s
u
l
t
m
o
d
5
;
此时
r
e
s
u
l
t
就等于
8
16
m
o
d
5
=
1
\begin{align*} 设:result &= 8^1 \quad mod \quad5 = 8 \quad mod \quad5 = 3\\ 8^2\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^2\quad mod\quad5 = 4\\ 8^4\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^4\quad mod\quad5 = 1\\ 8^8\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^8\quad mod\quad5 = 1\\ 8^{16}\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^{16}\quad mod\quad5 = 1 \end{align*}
设:result82mod5等于result84mod5等于result88mod5等于result816mod5等于result=81mod5=8mod5=3=result∗resultmod5;此时result就等于82mod5=4=result∗resultmod5;此时result就等于84mod5=1=result∗resultmod5;此时result就等于88mod5=1=result∗resultmod5;此时result就等于816mod5=1
8
25
m
o
d
5
8^{25}\quad mod \quad 5
825mod5
循环 | 值 | 权重 | 赋值 | 权重取余结果 | 取余 | 说明 |
---|---|---|---|---|---|---|
1 | 1 | 1 | W= 8 % 5 | W=a | result = (result * 8) % 5; | 8%5 = 3 |
2 | 0 | 2 | W= (W * W) % c | W= 8 2 8^2 82 mod 3 = 4 | * | |
3 | 0 | 4 | W= (W * W) % c | W= 8 4 8^4 84 mod 3 = 1 | * | |
4 | 1 | 8 | W= (W * W) % c | W= 8 8 8^8 88 mod 3 = 1 | result = (result * W) % c | 3 * 1 % 5 = 3 |
5 | 1 | 16 | W= (W * W) % c | W= 8 16 8^{16} 816 mod 3 = 1 | result = (result * W) % c | 3 * 1 % 5 = 3 |
所以最终结果 8 25 m o d 5 = 3 8^{25}\quad mod \quad 5 = 3 825mod5=3
算法实现
这个取模数学公式证明了, a的任意次幂的模数,运算,可以通过先求模,然后再乘以其它的数,再次取模,结果是一致的
这段代码,写的是A*B的积取模
typedef unsigned long long ull;
int a_b_mod_c(int a, ull b, int c)
{
int result = 1;
int weight = a % c; // 这个位置的权重是1,所以是a^1次方
if(b==0) return 1 % c; // 指数为0时,任何数的0次方都为1,可以直接得出结果。
if(b & 0x1==1) result = weight % c; // 因为这个位置的值为1,将引用weight到result.
b >>= 1; // B的第1位的权重就是a,已经处理完成,就右移一位。
while(b)
{
weight = ((weight % c) * (weight % c)) % c; // 求指数2的N次方的余数
if(b & 0x1==1) result = (result * weight) % c; // 该位的值为1,则把该位对应的权重也计算到最终结果来。
b >>= 1;
}
return result;
}
//上面的代码可以简化一下
int a_b_mod_c(int a, ull b, int c)
{
int result = 1;
int weight = a % c;
while(b)
{
if(b & 0x1==1) result = (result * weight) % c; // 该位的值为1,则把该位对应的权重也计算到最终结果来。
b >>= 1;
weight = ((weight % c) * (weight % c)) % c; // 求指数2的N次方的余数
}
return result;
}
下面用更巧妙的方法实现,不过比较难理解。
int a_b_Mod_c1(int a, int b, int c)
{
int digit[32];
int i, k, resualt = 1;
i = 0;
while(b)
{
digit[i++] = b&0x00000001;
b >>= 1;
}
for(k = i-1; k >= 0; k--)
{
resualt = (resualt * resualt) % c;
if(digit[k] == 1) resualt = (resualt * a) % c;
}
return resualt;
}
下面的这个代码,效率就比较低了。
int a_b_Mod_c2(int a, int b, int c)
{
int i, result = 1;
//参数非法较验就不写了
if(b<0 || c<0 || a<0) return -1;
for(i = 0; i < b; i++) result = (result * a) % c;
return result % c;
}
可以看到高效的幂取余运算基本就是O(1)的常量级的