1. 程式人生 > 實用技巧 >快速冪與快速乘

快速冪與快速乘

a^b

題目描述

求 a 的 b 次方對 p 取模的值,其中 0≤a,b≤10^9 , 0<p≤10^9

輸入
三個用空格隔開的整數a,b和p。

輸出
一個整數,表示a^b mod p的值。

樣例輸入
2 3 9

樣例輸出
8

思路

普通求冪時間複雜度為O(b),會TLE
設b的二進位制表示有k位,ci為0或1,則

\[b = \sum\limits_{i=0}^{k-1} c_i*2^i \]

\[a^b = a^{\sum\limits_{i=0}^{k-1} c_i*2^i} = \prod_{k=0}^{k-1}a^{c_{i}*2^i} = \prod_{k=0}^{k-1}{(a^{2^i})}^{c_i} \]

\[a^{2^i} = a^{2^{i-1}}*a^{2^{i-1}} \]

所以計算k-1次就可以求出答案,時間複雜度優化到O(logb)

還可以用遞迴的思想

\[a^b = \left\{ \begin{aligned} & a^{\frac b2}*a^{\frac b2} &(b為偶數) \\ & a^{\frac {b-1}2}*a^{\frac {b-1}2}*a& (b為奇數)\\ \end{aligned} \right. \]

程式碼

非遞迴寫法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_power(ll a, ll b, ll p){ // 快速冪
    ll ans = 1 % p;
    while(b){
        if(b & 1) ans = a*ans % p;
        a = a*a % p;
        b >>= 1;
    }
    return ans;
}
int main(){
    
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_power(a,b,p);
    cout<<ans;
}

遞迴寫法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_power(ll a, ll b, ll p){ //快速冪
    if(b == 0) return 1 % p;
    ll res = quick_power(a,b>>1,p)%p;
    if(b&1) return (res * res % p) * a % p; 
    else return  res * res % p;

}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_power(a, b, p);
    cout<<ans;
}

64位整數乘法

題目描述

求a乘b對p取模的值,其中1≤a,b,p≤1018

輸入
輸入3個long long型整數,a,b,p

輸出
輸出a*b%p的值

樣例輸入
250182048980811753
413715569939057660
133223633696258584

樣例輸出
19308689043391716

思路

與快速冪類似

\[b = \sum\limits_{i=0}^{k-1} c_i*2^i a*b = a*\sum\limits_{i=0}^{k-1} c_i*2^i = \sum\limits_{i=0}^{k-1} c_i*(a*2^i) \]

\[a*2^i = a*2^{i-1}*2 \]

同樣也可以用遞迴的思想

\[a*b = \left\{ \begin{aligned} & a*{\frac b2} + a*{\frac b2}&(b為偶數) \\ & a*{\frac {b-1}2} + a*{\frac {b-1}2}+a& (b為奇數)\\ \end{aligned} \right. \]

程式碼

非遞迴寫法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_mul(ll a, ll b, ll p){ // 快速乘
    ll ans = 0;
    while(b){
        if(b&1) ans = (ans + a) % p;
        a = a*2%p;
        b >>= 1;
    }
    return ans;
}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_mul(a,b,p);
    cout<<ans;
}

遞迴寫法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_mul(ll a, ll b, ll p){ // 快速乘
    if(b == 0) return 0;
    ll res = quick_mul(a,b>>1,p) %p;
    if(b & 1) return ((res+res)%p+a) %p;
    else return (res+res)%p; 
}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_mul(a,b,p);
    cout<<ans;
}