目錄
1 問題描述
我們發現,在int型下使用pow函數求5的三次方,結果爲124。
2 原因分析
pow函數的返回值爲double型,因浮點數長度問題,存在截斷誤差。
3 解決方法
將變量定義爲double型
有沒有更快求冪的方法?
4 快速冪講解
假設我們要求a^b,按照樸素算法就是把a連乘b次,這樣一來時間複雜度是O(b),即是O(n)級別。但快速冪能做到O(logn)的複雜度。
快速冪:
對於二進制的位運算,我們需要用到 "&" 與 ">>" 運算符,詳見位運算符的應用
先上實現快速冪運算的具體代碼:
long long ksm(long long a, long long b) {
long long ans = 1, base = a;
while(b != 0) {
if(b & 1 != 0) {
ans *= base;
}
base *= base;
b >>= 1;
}
return ans;
}
其中“b & 1”指取b的二進制數的最末位,如11的二進制數爲1011,第一次循環,取的是最右邊的“1” ,以此類推。
而“b >>= 1”等效於b = b >> 1,即右移1位,刪去最低位。
以a^11爲例:
b的二進制數爲1011,二進制從右向左算,但乘出來的順序是,是從左向右的。我們不斷的讓目的是累乘,以便隨時對ans做出貢獻。
要理解這一步:因爲 == ,下一步再乘,就是() * () == ,然後同理() * () == ,由此可以做到→ → → → → .......指數正好是 。再看上面的例子, = ,這三項就可以完美解決了,快速冪就是這樣。
如還有不明白的地方,建議手動模擬代碼的運行過程。
5 快速乘講解
我們知道,在計算機中做加法運算會比乘法快得多(參考模電中的加法器),做乘法運算往往會溢出,即使用long long類型也拯救不了。因此需要尋找一種能高效完成乘法運算且不會溢出的算法,這就是快速乘算法。
快速乘與快速冪原理相似,也是將運算轉換爲二進制處理:
以a * 11爲例:
就是把快速冪中的 * 號改爲+號
long long ksc(long long a, long long b) {
long long ans = 0;
while(b != 0) {
if(b & 1 != 0) {
ans += a;
}
a += a;
b >>= 1;
}
return ans;
}
此版本的複雜度和快速冪一樣,也是O(logn)。如果需要特別卡常數,可以去了解O(1)版本的快速乘。
6 完整代碼
爲了防止溢出,一般快速冪和快速乘的算法會在mod下運用,下面給出取模運算代碼。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e7;
//a ^ b
ll ksm(ll a, ll b, ll mod) {
ll ans = 1, base = a;
while(b != 0) {
if(b & 1 != 0) {
ans = (ans * base) % mod;
}
base = (base * base) % mod;
b >>= 1;
}
return ans;
}
//a * b
ll ksc(ll a, ll b, ll mod) {
ll ans = 0;
while(b != 0) {
if(b & 1 != 0) {
ans = (ans + a) % mod;
}
a = (a + a) % mod;
b >>= 1;
}
return ans;
}
int main() {
cout << "5 ^ 3 = " << ksm(5, 3, mod) << endl;
cout << "345352 * 11 = " << ksc(345352, 11, mod) << endl;
return 0;
}
運算結果:
7 References
以上,有問題歡迎指正!