【算法】快速冪運算


在計算 x時,我們會怎么算呢?如果只是x * x * x * ... * x 這樣每個數乘起來計算 n 次的的話,雖然算法簡單,但是復雜度為 O(n) ,往往不能滿足要求。讓我們來考慮加速冪運算的方法。

如果 n = 2k ,可以將其表示為  xn = ((x2)2)... ,只要做 k 次平方運算就可以輕松求得。由此我們想到,先將 n 表示為2的冪次的和 n = 2k1 + 2k2 + 2k3 + ... ,就有 xn = x2^k1 x2^k2 x2^k3 ... ,只要在依次求 x2^i 的同時計算就好了,最終得到了 O(logn) 計算冪運算的算法。比如計算x22,可以把 x22 表示為 x* x4 * x16 (22轉成二進制是10110)。在進行冪運算時,往往因為結果數字過大,而讓我們輸出取余后的結果。下面是一段參考代碼:

 1 typedef long long ll;
 2 
 3 ll mod_pow(ll x,ll n,ll mod){
 4     ll res = 1;
 5     while(n>0){
 6         if(n&1) res = res * x % mod;  //如果二進制最低位為1,則乘上x^(2^i)
 7         x = x * x % mod;  //將x平方
 8         n >>= 1;
 9     }
10     return res;
11 }

代碼還是很容易理解的,先把n轉化成二進制表示,然后每一位開始依次計算。如果二進制最低位為1,那么就將結果乘上x2^i ( i 的值取決於現在算到哪一位,如果是第0位則 i = 0)。每次將x平方,然后將n右移一位,繼續下一位的計算。比如計算x22,就先把22轉化成10110,最低位是0,res不變,x變成x2,右移變成1011;最低位是1,res乘上x2,x2再變為x4(也就是x2^2),右移變成101;最低位是1,res乘上x4,x4再變為x8,右移變成10……依次類推,最終結果也就是res = x* x4 * x16(此處沒有考慮取余)。

 

---------------------------------------------下面是另一種思路。--------------------------------------------------------------

當n為偶數時有 xn = ((x2)(n/2)) ,遞歸轉為n/2的情況;n為奇數時有 xn = ((x2)(n/2)) * x ,同樣也遞歸轉為 n/2 的情況。這樣不斷遞歸下去,每次n都減半,於是可以在O(logn)時間內完成冪運算。這個比第一種似乎更容易想到也更易理解,下面給出代碼:

 1 typedef long long ll;
 2 
 3 ll mod_pow(ll x,ll n,ll mod){
 4     if(n == 0) return 1;
 5     if(n == 1) return  x % mod;
 6     ll res = mod_pow(x * x % mod, n / 2, mod);
 7     if(n & 1)
 8         res = res * x % mod;
 9     return res;
10 }

 

還有一種非常類似的,f(x,n) = xn,x為奇數那么f(x,n) = f(x,n/2) * f(x,n/2) *x,x為偶數那么f(x,n) = f(x,n/2) * f(x,n/2)。

1 ll f(int x,int n){
2     if(n==0) return 1;
3     if(n==1) return x;
4     if(n&1) return f(x,n>>1)*f(x,n>>1)*x;  //如果n是奇數
5     else return f(x,n>>1)*f(x,n>>1);   //如果n是偶數
6 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM