快速幂取模算法
所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余)。在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快、计算范围更大的算法,产生了快速幂取模算法。我们先从简单的例子入手:求a^b mod c
算法1.直接设计这个算法:
int ans = 1;
for(int i = 1;i<=b;i++)
{
ans = ans * a;
}
ans = ans % c;
缺点:这个算法存在着明显的问题,如果a和b过大,很容易就会溢出。
我们先来看看第一个改进方案:在讲这个方案之前,要先看这样一个公式:a^b mod c = (a mod c)^c mod c
于是不用思考的进行了改进:算法2.改进算法:
int ans = 1;
a = a % c; //加上这一句
for(int i = 1;i<=b;i++)
{
ans = ans * a;
}
ans = ans % c;
- 读者应该可以想到,既然某个因子取余之后相乘再取余保持余数不变,那么新算得的ans也可以进行取余,所以得到比较良好的改进版本。
算法3.进一步改进算法:
int ans = 1;
a = a % c; //加上这一句
for(int i = 1;i<=b;i++)
{
ans = (ans * a) % c;//这里再取了一次余
}
ans = ans % c;
这个算法在时间复杂度上没有改进,仍为O(b),不过已经好很多的,但是在c过大的条件下,还是很有可能超时,所以,我们推出以下的快速幂算法。
算法4.快速幂算法:
快速幂算法依赖于以下明显的公式:
另一种证明方式:
int PowerMod(int a, int b, int c)
{
int ans = 1;
a = a % c;
while(b>0) {
if(b % 2 = = 1)
ans = (ans * a) % c;
b = b/2;
a = (a * a) % c;
}
return ans;
}
本算法的时间复杂度为O(logb),能在几乎所有的程序设计(竞赛)过程中通过,是目前最常用的算法之一。
作者:wuyudong
出处:http://www.cnblogs.com/wuyudong/
ps:本文参考自网络
引申例题:
2011
总时间限制: 1000ms 内存限制: 65536kB
描述
已知长度最大为200位的正整数n,请求出2011^n的后四位。
输入
第一行为一个正整数k,代表有k组数据,k<=200接下来的k行,
每行都有一个正整数n,n的位数<=200
输出
每一个n的结果为一个整数占一行,若不足4位,去除高位多余的0
样例输入
3
5
28
792
样例输出
1051
81
5521
思路:快速幂的经典应用。就是求2011的n次方模10000的余数。
高精度方法:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int b,g,t;
char s[300];
bool f[5005];
int as[300];
void fff()
{
int k;
for(k=t;k>=1;k--)
{
if(as[k]%2==1) as[k-1]+=10;
as[k]/=2;
}
if(as[t]==0) t--;
if(as[0]%2==1) {g=1;as[0]/=2;}
else{g=0;as[0]/=2;}
}
int qmd(int a,int m)
{
b=1;
while(t>0||as[0]>=1)
{
fff();
if(g%2==1) b=a*b%m;
a=a*a%m;
}
printf("%d\n",b);
}
int main()
{
int n;
int w,e,r;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
t=strlen(s);
int p=t-1;
while(p>=0)
{
as[t-1-p]=s[p]-'0';
p--;
}
t--;
qmd(2011,10000);
}
return 0;
}
比较神的方法,先打了一个模10000快速幂,发现幂是751,幂是4751,幂是4484484751,幂是4984465461751的答案都是一样的。规律就是后三位只要相同,得到的模10000的数就相同。200位的数什么类型都爆了。所以字符串读入,然后取后三位即可(别忘了本来位数就小于3位的情况啊)。然后快速幂。。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[206];
int n,k;
int ans[205];
int ksm(int a,int b,int c)
{
int ans=1;
a=a%c;
while(b>0)
{
if(b%2==1)
ans=(ans*a)%c;
b=b/2;
a=(a*a)%c;
}
return ans;
}
int main()
{
int i,j;
cin>>k;
for (j=1;j<=k;j++)
{
n=0;
memset(s,'\0',sizeof(s));
cin>>s;
if (strlen(s)==1) n=s[0]-'0';
else if (strlen(s)==2) n=(s[0]-'0')*10+s[1]-'0';
else if (strlen(s)==3) n=(s[0]-'0')*100+(s[1]-'0')*10+s[2]-'0';
else
for(int i=strlen(s)-4;i<=strlen(s)-1;i++) n=n*10+(s[i]-'0');
cout<<ksm(2011,n,10000)<<endl;
}
}