,快速乘,快速冪,矩陣快速冪(求斐波那契數列)
快速冪:
方法一::
首先快速冪有幾個公式:
1.(a^b)mod c=( a mod c)^b mod c;
(ab) mod c=[(a mod c)*( b mod c)] mod c; (積的取餘等於取餘的積取餘)
快速冪演算法依賴於一下兩個公式:
a^b mod c=((a²)^(b/2)) mod c , b是偶數
a^b mod c=((a²)(b/2)*a) mod c ,b是奇數
有了上述兩個公式,我們可以得出一下結論:
1.如果b是偶數,我們可以記k=a²modc,那麼(k)^(b/2)mod c 就可以了.
2.如果b是奇數,我們也可以記k=a²modc,那麼((k)^(b/2)mod c就可以了
所以有了最終的演算法:快速冪演算法(log b):
int ans=1;
a=a%c;
while(b>0)
{
if(b%2==1) ans=(ans*a)%c;
b=b/2;
a=(a*a)%c;
}
將上述程式碼結構化,也就是寫成函式:
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;
}
方法二::
用位運算來實現:
1.b&1(取b的二進位制最低位(即第0位) 判斷b是否為奇數,是則為1
2.b>>1(去掉b的二進位制最低位(即第0位)
)
所以程式碼實現為:
#include<iostream> #include<cstdio> using namespace std; int pow3(int x,int n) { if(n==0) return 1; else { while((n&1)==0) { n>>=1; x*=x; } } cout<<n<<endl; int result=x; n>>=1; while(n!=0) { x*=x; cout<<x<<endl; if((n&1)!=0) result*=x; n>>=1; } return result; } int main() { int x,n; cin>>x>>n; cout<<pow3(x,n)<<endl; }
矩陣快速冪:
矩陣快速冪其實就是矩陣相乘然後套用快速冪.這個首先來寫一下矩陣相乘
const int N=100;
int c[N][N];
void multi(int a[][N],int b[][N],int n)
{
memset(c,0,sizeof c);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
c[i][j]+=a[i][k]*b[k][j];
}
然後瞭解一下矩陣相乘原理:兩個相乘是要一行和一列對應乘,那麼矩陣乘法是需要A的行數與B的列數相等的,矩陣快速冪只會用到方陣(n*n)
矩陣快速冪是用來求解遞推式的,所以第一步先要列出遞推式:
f(n)=f(n-1)+f(n-2)
第二步是建立矩陣遞推式,找到轉移矩陣:
,簡寫成T * A(n-1)=A(n),T矩陣就是那個2*2的常數矩陣,而
這裡就是個矩陣乘法等式左邊:1*f(n-1)+1*f(n-2)=f(n);1*f(n-1)+0*f(n-2)=f(n-1);
所以這裡相乘就是矩陣n-1次相乘,然後輸出第一行第二個元素,也就是a[0][1]這裡可以練習一下poj 3070,其實就是矩陣快速冪的模板題
然後給一些簡單的遞推式:
1.f(n)=a*f(n-1)+b*f(n-2)+c;(a,b,c是常數)
2.f(n)=c^n-f(n-1) ;(c是常數)
快速乘:
問題:
求 (a*b) % m 的值,其中 a,b,m 是1到10^18。
如果直接乘的話,因為a和b還有m都很大,那麼會溢位long long,所以需要一些方法。
樸素的想法是用陣列模擬高精度,但是比較麻煩。
還有更好的方法:
求乘法的列豎式,
1234*213=1234*3+1234*10*1+1234*10^2*2;
那麼如果變成二進位制的話 10101 × 1011 = 10101*1+10101*2^1*1+10101*2^2*0+10101*2^3*1;
這樣程式碼如下:
long long multi(long long a,long long b,long long m) {
long long ans=0;
while(b) {
if(b&1) (ans+=a) %= m;
(a=a*2) %= m;
b/=2;
}
return ans;
}