快速幂(良心教程)

一、问题引入

求(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的值比较大的话,就很难在题目规定的时间内计算出结果。

二、快速幂

其实可以利用分治思想对暴力法进行优化,我们可以这样想:
a b = { a b / 2 a b / 2 , if  b  is even a b / 2 a b / 2 a , if  b  is odd a^b = \begin{cases} a^{b/2}*a^{b/2}, & \text {if $b$ is even} \\ a^{b/2}*a^{b/2}*a, & \text{if $b$ is odd} \end{cases}
注意:even是偶数,odd是奇数,b/2的结果向下取整
这样划分,我们只要求 a b / 2 a^{b/2} 的值就可以的通过一次乘法运算求出 a b a^b 的值。而 a b / 2 a^{b/2} 又可以进一步分解为 a b / 4 a^{b/4} a b / 8 a^{b/8} ……直到 a 1 a^1 ,这就是快速幂,这样的操作的时间复杂度仅为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;
}

注释部分是位运算优化,位运算的运算效率更高。

四、应用

  1. 快速幂用于求解规模较大的求幂问题
    例如:求(a^b) mod n
  2. 还会结合矩阵乘法求解递推式求值问题
    例如:Fibonacci数列的第n项
    我们知道:
    F(n)=F(n-1)+F(n-2)
    F(n-1)=F(n-1)+0*F(n-2)
    转化成矩阵运算可得

( F ( n ) F ( n 1 ) ) = ( 1 1 1 0 ) ( F ( n 1 ) F ( n 2 ) ) \begin{pmatrix} F(n) \\ F (n-1)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} F(n-1) \\F(n-2)\\ \end{pmatrix}
而右边的21阶矩阵又可以进一步分解为
( F ( n 1 ) F ( n 2 ) ) = ( 1 1 1 0 ) ( 1 1 1 0 ) ( F ( n 2 ) F ( n 3 ) ) \begin{pmatrix} F(n-1) \\ F (n-2)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} F(n-2) \\F(n-3)\\ \end{pmatrix}
按照这样一直分解下去直到右边的2
1阶矩阵F(2),F(1),即
( F ( n ) F ( n 1 ) ) = ( 1 1 1 0 ) n 2 ( F ( 2 ) F ( 1 ) ) \begin{pmatrix} F(n) \\ F (n-1)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}^{n-2}* \begin{pmatrix} F(2) \\F(1)\\ \end{pmatrix}
这时利用矩阵版的快速幂求解其中的矩阵幂乘,就可以在相对较小的时间复杂度下得出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);
	}
}

//相比较之下还是循环版的运行效率比较高

发布了59 篇原创文章 · 获赞 103 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42292229/article/details/86742650
今日推荐