快速幂算法
就是求一个高精度幂次取余时的一个快速算法
比如我们要求
a
b
a^b
ab%m的时候
应用于快速幂算法 也就是将O(n)转化为O(logn)的算法
他的原理就是:
2 5 2^5 25 = 2 ∗ 2 ∗ 2 ∗ 2 ∗ 2 2*2*2*2*2 2∗2∗2∗2∗2 = 2 101 2^{101} 2101 = 2 1 ∗ 2 4 2^1 * 2^4 21∗24
相当于我们只需要遍历幂次的二进制位的长度就可以了
我们知道任意数x的二进制位长度是log2(x)
这样大大提升了求幂的效率
可是这样运算容易导致数据溢出
如何保证运算不超出数据范围还能正确求余呢?
我们需要借助同余定理 解决在这个求幂的运算过程中所产生的高精度问题
如
2
5
2^5
25%m
同余定理 就是给你定一个正整数m(m>1) 如果两个整数 a 和b 满足 a-b能够被m整除 则(a-b)/m
得到一个整数 则称a,b对模m同余
同余定理两个应用是
(a+b)%m = (a%m+b%m)%m
(
a
∗
b
a * b
a∗b)%m = (a%m
∗
*
∗ b%m)%m
以上两条等式可以分别证明
这两条性质 可以应用在高精度运算中
实际上高精度就是说参与运算的数据和运算结果的范围, 超出标准数据类型能表示的数据大小范围的运算。
这个时候,如果要得到正确的计算结果,显然不能依靠普通方法实现了。 而要在普通运算原理的基础上,加以辅助算法来实现超大数据的计算。
例如:求两个100位的数据的和,或者计算两个100位的数字乘积。这时就要用到高精度算法了。
下面我们来看下代码
typedef long long ll;
ll quickpow(ll a,ll b,ll m){
ll ans = 1;
while(b){
if(b%2)ans*=a;
a*=a,a%=m;//套括号取模
ans%=m;//套括号取模
b>>=1;
}
return ans%m;//套括号取模
}
以上代码如果用表达式表达出来的话 就是
((
a
n
s
∗
a
ans*a
ans∗a) % m
∗
(
a
∗
a
)
* ( a * a )
∗(a∗a) % m
∗
(
a
∗
a
)
* ( a * a )
∗(a∗a) % m ) % m =
1
∗
2
5
1 *2^5
1∗25 %m
这个完全就是我们上面写的那个同余定理的应用中对高精度取余的处理方式
那么也就是
(
(
1
∗
2
)
∗
(
2
∗
2
)
∗
(
2
∗
2
)
)
((1*2)*(2*2)*(2*2))
((1∗2)∗(2∗2)∗(2∗2))%m =
[
(
1
∗
2
)
[(1*2)
[(1∗2)%m
∗
(
(
2
∗
2
)
* (( 2*2)
∗((2∗2)%m
∗
(
2
∗
2
)
*(2*2)
∗(2∗2)%m
)
)
)%m
]
]
]%m
所以在这个过程中既不会低效 也不会导致数据溢出
这就是快速幂算法