1. 程式人生 > >快速冪+快速冪經典例題

快速冪+快速冪經典例題

快速冪取模演算法

所謂的快速冪,實際上是快速冪取模的縮寫,簡單的說,就是快速的求一個冪式的模(餘)。在程式設計過程中,經常要去求一些大數對於某個數的餘數,為了得到更快、計算範圍更大的演算法,產生了快速冪取模演算法。我們先從簡單的例子入手:求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),能在幾乎所有的程式設計(競賽)過程中通過,是目前最常用的演算法之一。

引申例題:
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;
    }
}