快速幂取模算法

快速幂取模算法

所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余)。在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快、计算范围更大的算法,产生了快速幂取模算法。我们先从简单的例子入手:求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 
样例输入 


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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值