快速幂、矩阵快速幂+斐波那契数列(Fibonacci Sequence)



1、快速幂


什么是幂?

可能有人对这个概念并不很清楚。我们来简单的回顾一下。比如n个a相乘,那我们平常的数学表示就是a^n,读作a的n次幂。那么a就是幂的底,n就是幂指数。

快速幂是什么?

别急,我们通过例子来讲解。
假如需要求A^9。我们的常规做法一定是9个数A相乘算出结果,简单粗暴还很好理解,但是当我们把9该成1000000的时候,你们还会觉得简单吗?而且你会发现很耗费时间。
对此,快速幂求解就给了我们一个非常快捷的方法。将之前的时间复杂度O(n)降低到了O(log2(n))。

还是借助上面的一个例子:A^9
A^9 = A*A*A*A*A*A*A*A 【我们需要计算8次算出结果】
现在做一些改动:
A^9 = (A*A)*(A*A)*(A*A)*(A*A)*A = ((A^2)^4)*A【计算平方、计算四次方、计算最后一个数A,总共需要5次计算】
再做进一步的改动:
A^9 = ((A*A)*(A*A))*((A*A)*(A*A))*A = (((A^2)^2)^2)*A【计算平方、再平方、在平方、计算最后一个A,总共需要4次】

经过这样的改变之后,我们的计算次数明显下降了很多。

下面是用循环实现快速幂代码:

/**
 * 快速幂
 * @param n 幂指数
 * @param a 幂底数
 */
public int quickPower(int a ,int n){
	int ans = 1,base = a;
	while(n > 0){
		if(n%2 != 0)//判断n是否为奇数,也可以写成(n&1 != 0)
			ans = base*ans;
		base = base*base;
		n = n/2;//对n除以2,也可以写成(n = n>>1)
	}
	return ans;
}

下面是使用递归实现快速幂代码:

public int quickPower(int a ,int n){
	if(n == 0){
		return 1;
	}
	if(n%2 != 0){//当n为奇数的时候
		return quickPower(a, n/2)*quickPower(a, n/2)*a;
	}else{
		return quickPower(a,n/2)*quickPower(a, n/2);
	}
}

从上面的代码可以看出,快速幂也是一种二分的思想。

当我们的幂指数非常大的时候,你会发现基本的数据类型根本保存不了算出来的结果。


2、矩阵快速幂


矩阵快速幂其实和快速幂是大同小异。只是将幂底变成了矩阵,这样来达到快速计算矩阵的目的。

矩阵的乘法

对于学习过线性代数的人应该比较清楚矩阵的乘法。
假如矩阵A与矩阵B相乘,那必须满足的条件就是,矩阵A的列数要等于矩阵B的行数。
对于一个m行n列的矩阵A乘以一个n行p列的矩阵B结果是一个m行p列的矩阵。

矩阵满足的一些基本定律:
结合律:A*(B*C) = (A*B)*C
分配律:(A+B) *C =A*C+B*C   C*(A+B) = C*A+C*B
数乘结合律:k*A*B = A*(k*B) =(k*A)*B
转置定律:(A*B)' = B' * A'

下面用代码实现2行2列矩阵的乘法:
public class Matrix {
	private int matrix[][] = new int[2][2];//定义一个2行2列的矩阵
	
	public Matrix matrixMulti(Matrix a,Matrix b){
		Matrix result = new Matrix();
		for(int i=0 ;i<2;i++){
			for(int j=0;j<2;j++){
				for(int k=0;k<2;k++){
					result.matrix[j][i] += a.matrix[j][k]*b.matrix[k][i];
				}
			}
		}
		return result;
	}
}

矩阵计算快速幂

矩阵计算快速幂其实和快速的幂的方法差不多,用循环实现矩阵快速幂需要构建一个单位矩阵,而且矩阵的乘法计算要借助上面矩阵乘法的函数来实现。

用代码实现矩阵快速幂:
public class Matrix {
	private int matrix[][] = new int[2][2];//定义一个2行2列的矩阵
	
	Matrix matrixQuickPower(int n)//还是小范围数据来说吧,要不然返回值的类型自己定义  
	{  
	    Matrix result = new Matrix();
	    Matrix a = new Matrix();    //需要实现多次幂的矩阵 
	    a.matrix[0][0]=1;//给矩阵赋初值  
	    a.matrix[0][1]=1;  
	    a.matrix[1][0]=1;  
	    a.matrix[1][1]=0;  
	    for(int i=0;i<2;i++){
	    	result.matrix[i][i] = 1;//将结果矩阵初始化为一个单位矩阵
	    } 
	    while(n>0)  
	    {  
	        if((n&1) != 0) result=matrixMulti(result,a);//这里看就要用到上面的矩阵相乘了;  
	        a=matrixMulti(a,a);  
	        n=n>>1;  
	    }  
	    return result;  
	}
}

3、斐波那契数列(Fibonacci Sequence)

有人可能不太明白斐波那契数列和矩阵的快速幂有什么关系。

首先我们来看看斐波那契数列的定义:

斐波那契数列(Fibonacci Sequence),又称黄金分割数列。其递推公式为如下

f(n) = f(n-1)+f(n-2)  f(0)=1  f(1)=1(n>=2)

然后将斐波那契数列转换为矩阵的运算我们会发现


下面是一个完整的结合斐波那契数列的矩阵快速幂的代码:

public class Matrix {
	private int matrix[][] = new int[2][2];//定义一个2行2列的矩阵
	private static int mod = 100;
	
	public Matrix matrixMulti(Matrix a,Matrix b){
		Matrix result = new Matrix();
		for(int i=0 ;i<2;i++){
			for(int j=0;j<2;j++){
				for(int k=0;k<2;k++){
					result.matrix[j][i] += a.matrix[j][k]*b.matrix[k][i];
					//result.matrix[i][j] %=mod; //这是特别重要的一步,一边算一遍取模。
				}
			}
		}
		return result;
	}
	
	Matrix matrixQuickPower(int n)//还是小范围数据来说吧,要不然返回值的类型自己定义  
	{  
	    Matrix result = new Matrix();
	    Matrix a = new Matrix();    //需要实现多次幂的矩阵 
	    a.matrix[0][0]=1;//给矩阵赋初值  
	    a.matrix[0][1]=1;  
	    a.matrix[1][0]=1;  
	    a.matrix[1][1]=0;  
	    for(int i=0;i<2;i++){
	    	result.matrix[i][i] = 1;//将结果矩阵初始化为一个单位矩阵
	    } 
	    while(n>0)  
	    {  
	        if((n&1) != 0) result=matrixMulti(result,a);//这里看就要用到上面的矩阵相乘了;  
	        a=matrixMulti(a,a);  
	        n=n>>1;  
	    }  
	    return result;  
	}
}

在上面的代码中,我们有一点需要相当的注意的就是边算边取模,也就是我注释掉的那一行代码。为什么呢?假如我们现在需要计算(A^1000000)%mod(A就是我们初始化的矩阵)。如果我们按着原来的方法计算,算着算着我们发现算到一定的程度时数据太大,我们没有办法用基本数据类型保存这些数据了,所以我们就需要边算边求模。

讲到这里,为了进一步解释为什么需要边算边求模,我们需要了解一个知识点:同余模定理。

在同余模定理中有一个定律就解释了边算边取模的正确性:

(a*b)%d = (a%d * b%d)%d


在矩阵快速幂中难点就是要自己构造矩阵,至于计算可以套模版。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值