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