一、问题引入
求(a^b) mod n的结果
分析
- 思路:看到这样的题目,我们最容易想到就是直接写一个b次的循环,每次循环乘上一个a,最后再对结果mod n就能得到结果。
- 改错:为了防止数据溢出,我们利用取模的性质(a x b) mod n=((a mod n)x(b mod n))mod n,循环中每次乘a都mod n一次。
- 缺陷:这种暴力法的时间复杂度是O(b),如果b的值比较大的话,就很难在题目规定的时间内计算出结果。
二、快速幂
其实可以利用分治思想对暴力法进行优化,我们可以这样想:
(注意:even是偶数,odd是奇数,b/2的结果向下取整)
这样划分,我们只要求
的值就可以的通过一次乘法运算求出
的值。而
又可以进一步分解为
、
……直到
,这就是快速幂,这样的操作的时间复杂度仅为O(logb)。
三、代码实现
递归版
long quickPow(long a,long b, long n) {
if(b==0)
return 1;
if(b%2==1) {
long t=quickPow(a, b/2, n);
t=(t*t)%n;
t=(t*a)%n;
return t;
}else {
long t=quickPow(a, b/2, n);
return (t*t)%n;
}
}
循环版
long quickPow(long a,long b, long n) {
long s=1;
while(b!=0){
if(b%2==1) //b&1
s=(s*a)%n;
a=(a*a)%n;
b/=2; //b>>=1
}
return s;
}
注释部分是位运算优化,位运算的运算效率更高。
四、应用
- 快速幂用于求解规模较大的求幂问题
例如:求(a^b) mod n - 还会结合矩阵乘法求解递推式求值问题
例如:Fibonacci数列的第n项
我们知道:
F(n)=F(n-1)+F(n-2)
F(n-1)=F(n-1)+0*F(n-2)
转化成矩阵运算可得
而右边的21阶矩阵又可以进一步分解为
按照这样一直分解下去直到右边的21阶矩阵F(2),F(1),即
这时利用矩阵版的快速幂求解其中的矩阵幂乘,就可以在相对较小的时间复杂度下得出Fibonacci数列的第n项的值。
五、实战演练
传送门
这是来自洛谷一道比较简单的模板题,主要是用于检测快速幂的模板代码
题目描述
输入b,p,k的值,求b^p mod k的值。其中b,p,k*k为长整型数。
输入格式:
三个整数b,p,k.
输出格式:
输出“b^p mod k=s”
s为运算结果
输入样例#1:
2 10 9
输出样例#1:
2^10 mod 9=7
代码如下
import java.util.*;
public class P1226 {
static long a,b,n;
static long quickPow(long a,long b, long n) {
long s=1;
while(b!=0) {
if(b%2==1) {
s=(s*a)%n;
}
a=(a*a)%n;
b/=2;
}
return s;
}
// static long quickPow(long a,long b, long n) {
// if(b==0)
// return 1;
// if(b%2==1) {
// long t=quickPow(a, b/2, n);
// t=(t*t)%n;
// t=(t*a)%n;
// return t;
// }else {
// long t=quickPow(a, b/2, n);
// return (t*t)%n;
// }
// }
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
a=cin.nextLong();
b=cin.nextLong();
n=cin.nextLong();
System.out.println(a+"^"+b+" mod "+n+"="+quickPow(a, b, n)%n);
}
}
//相比较之下还是循环版的运行效率比较高
扫描二维码关注公众号,回复:
9077300 查看本文章