您的位置:首页 > 其它

菜鸟上路 杭电OJ 1005 从 斐波那契到 快速幂

2016-05-20 17:39 369 查看
先来看一看题目:

Problem Description

A number sequence is defined as follows:

f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7.

Given A, B, and n, you are to calculate the value of f(n).

 

Input

The input consists of multiple test cases. Each test case contains 3 integers A, B and n on a single line (1 <= A, B <= 1000, 1 <= n <= 100,000,000). Three zeros signal the end of input and this test case is not to be processed.

 

Output

For each test case, print the value of f(n) on a single line.

 

Sample Input

1 1 3
1 2 10
0 0 0

 

Sample Output

2
5

 

Author

CHEN, Shunbao

这道题起初采用递归来接,显然地超时,毕竟n可以取到100000000,递归肯定没法解决。

接着,我采用了迭代,还是超时,没有办法。

去网上搜索资料发现,斐波那契数列可以转化为求矩阵幂的问题:



只要求得最后那个矩阵,问题也就解决了。

而求高次幂矩阵涉及到快速幂的知识。

于是,现场学了一波快速幂。

说快速幂,又分为普通数的快速幂和矩阵的快速幂,其实两者道理是想通的。

看下面这篇文章:


快速乘法/幂 算法详解

适用范围:快速计算a*b % mod的结果,或者快速计算a^b % mod 的结果,时间复杂度大大降低。
算法描述:首先你可能会问a*b不是直接乘就出来了么,为什么需要快速算法?但是乘法在计算机中处理的时间并不是这么快的,也要拆分为加法来做的。所以快速乘法会更快的计算a*b的结果,而且a*b%mod可能还没取模就已经爆long long,但快速乘法却不会。快速幂也是同样的道理。
实现的原理都是基于按照二进制位一步一步乘来避免重复的操作,利用前面的中间结果,从而实现快速的目的。
对于乘数b来说,势必可以拆成2进制,比如110101。有一些位为0,有一些位为1。根据乘法分配律:a*b=a*(b1+b2+b3+……) 那么对于a*53 = a*110101(二进制)= a*(100000+10000+100+1)=a*(100000*1+10000*1+1000*0+100*1+10*0+1*1)。
那么设立一个ans=0用于保存答案,每一位让a*=2,在根据b的对应为1看是不是加上此时的a,即可完成快速运算。比如刚才的例子让a=5,运转流程如下。



即可计算出5*53=265。
不知道看到这里你发现了没有,其实对于快速幂其实是一样的道理,只不过每一位a更新的时候不是*2,而是a=a*a,ans+变成ans*。 例如3^9的流程如下:3^5=3^(1001) (二进制)= 3^(1000*1+100*0+10*0+1*1) 


最后说一些细节吧,如果要取模在ans+、ans*、和a更新的时候都%mod即可。然后b一位一位的读取每次b/=2或者b>>=1,表示b向右移动一位,再与1做&操作即可。(见代码)
上代码:
#include
#include
#include
#include
#include
#include
using namespace std;

long long q_mul( long long a, long long b, long long mod ) //快速计算 (a*b) % mod
{
long long ans = 0;	// 初始化
while(b)				//根据b的每一位看加不加当前a
{
if(b & 1)			//如果当前位为1
{
b--;
ans =(ans+ a)%mod;   //ans+=a
}
b /= 2;							//b向前移位
a = (a + a) % mod;			//更新a

}
return ans;
}

long long q_pow( long long a, long long b, long long mod ) //快速计算 (a^b) % mod
{
long long ans = 1; // 初始化
while(b)//根据b的每一位看乘不乘当前a
{
if(b & 1)	//如果当前位为1
{
ans = q_mul( ans, a, mod ); //ans*=a
}
b /= 2;										//b向前移位
a = q_mul( a, a, mod );			//更新a
}
return ans;
}

int main( )
{
long long a, b, n;
while(cin >> a >> b >> n)
{
cout << "a*b%n = " << q_mul( a, b, n ) << endl;
cout << "a^b%n = " << q_pow( a, b, n ) << endl;
}
return 0;
}


转自:http://www.2cto.com/kf/201505/396902.html

另外一篇:http://wenku.baidu.com/link?url=E7gB0wqaeYUdQimtYIrKjCxGrE1xN3sAWkc0WjmcwErgfQA5JMc51j53GtJP796SWp6TF3A_lNAgjMm_5nJj9GJ_ZpHMzfz0d3yz7nT0qxO

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是网上为数不多能够讲明白快速幂的文章(对于我这个菜鸟来说)。最后注意一点:在举证乘法运算之后要立即取模,放在后序步骤取模的话,会因为原先矩阵运算结果太大,而造成WA的问题!!!。

最后贴出AC代码:

#include <iostream>
using namespace std;

int ** MUL(int **P,int **Q)
{
int ** M=new int* [2];
M[0]=new int [2];
M[1]=new int[2];
M[0][0]=(P[0][0]*Q[0][0]+P[0][1]*Q[1][0])%7; //算乘法的时候要及时取模!!
M[0][1]=(P[0][0]*Q[0][1]+P[0][1]*Q[1][1])%7;
M[1][0]=(P[1][0]*Q[0][0]+P[1][1]*Q[1][0])%7;
M[1][1]=(P[1][0]*Q[0][1]+P[1][1]*Q[1][1])%7;
return M;
}

int ** power(int ** P,int n) //矩阵快速求幂
{
int ** pw=new int * [2];
pw[0]=new int [2];
pw[1]=new int [2];
pw[0][0]=1;
pw[0][1]=0;
pw[1][0]=0;
pw[1][1]=1;
while(n>0)
{
if(n%2==1) //从低位到高位,从右往左
{
pw=MUL(pw,P);
}
P=MUL(P,P);
n/=2;
}
return pw;
}

int main()
{
int A,B,n,m;
cin>>A>>B>>n;
int ** index=new int *[2];
index[0]=new int [2];
index[1]=new int [2];
while(!(A==0&&B==0&&n==0))
{
if(n==1||n==2)
cout<<1<<endl;
else
{

index[0][0]=A;
index[0][1]=B;
index[1][0]=1;
index[1][1]=0;
index=power(index,n-2);
cout<<(index[0][0]+index[0][1])%7<<endl;
}
cin>>A>>B>>n;}
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM