【演算法】快速冪運算
阿新 • • 發佈:2018-12-25
在計算 xn 時,我們會怎麼算呢?如果只是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 ...
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
---------------------------------------------下面是另一種思路。--------------------------------------------------------------
當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 }