快速幂cpp代码

基础问题:求数字 aann 次方。
最基本的想法,就是一次一次相乘,时间复杂度为 O(n)O(n)
代码如下:

1
2
3
4
5
6
7
8
9
using ll = long long;
ll pow(ll a, ll n)
{
ll ret = 1;
for (ll i = 0; i < n; i++) {
ret *= a;
}
return ret;
}

稍微思考一下便可得知,计算一个数的nn次方并不需要相乘 nn 次。我们如果知道 aan/2n/2 次方,便可以用两个 an/2a^{n/2} 相乘,获得 ana^n。顺着这个思想往下想,可以考虑把 nn 拆成二进制的形式,这样只要把对应的二进制位数得到的幂相乘起来就可以得出答案了。举例:当 nn 为 7 时,我们要 676^777 按照二进制可以拆成 0000011100000111。那我们只要知道 616^1626^2646^4,再把他们相乘就可以了。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ll qpow(ll a, ll n)
{
if (n == 0) {
return 1;
} else if (n % 2 == 1) {
return qpow(a, n - 1) * a;
} else {
ll ret = qpow(a, n / 2);
return ret * ret;
}
}

ll qpow2(ll a, ll n)
{
ll ret = 1;
while (n) {
if (n & 1) {
ret *= a;
}
a *= a;
n >>= 1;
}
return ret;
}

一为递归的方式,二为迭代的方式。考虑到调用栈切换,方式二会快一些。这种把幂拆成二进制的方法,就被称为快速幂。
完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <time.h>
using namespace std;
using ll = long long;
ll pow(ll a, ll n)
{
ll ret = 1;
for (ll i = 0; i < n; i++) {
ret *= a;
}
return ret;
}

ll qpow(ll a, ll n)
{
if (n == 0) {
return 1;
} else if (n % 2 == 1) {
return qpow(a, n - 1) * a;
} else {
ll ret = qpow(a, n / 2);
return ret * ret;
}
}

ll qpow2(ll a, ll n)
{
ll ret = 1;
while (n) {
if (n & 1) {
ret *= a;
}
a *= a;
n >>= 1;
}
return ret;
}
int main()
{
ll a = 7, n = 16;
clock_t startTime, endTime;
startTime = clock(); // cpu clock time
cout << pow(a, n) << endl;
endTime = clock();
cout << (endTime - startTime) << endl;

startTime = clock(); // cpu clock time
cout << qpow(a, n) << endl;
endTime = clock();
cout << (endTime - startTime) << endl;

startTime = clock(); // cpu clock time
cout << qpow2(a, n) << endl;
endTime = clock();
cout << (endTime - startTime) << endl;
}

可以看到后面两种cpu周期普遍低于前一种。