普通乘方运算
原理
众所周知,如果我们想要计算 a n a^n an,最常规的方法是计算 a × a × … × a ⏟ n 个 a \underbrace{a \times a \times …\times a}_{n个a} n个a a×a×…×a。
这很容易用代码进行模拟,只需要用 for
循环即可:
代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
int Power(int a, int b)
{
int ans = 1;
for(int i = 1; i <= b; i = i + 1)
ans *= a;
return ans;
}
signed main()
{
int a, n;
cin >> a >> n;
cout << Power(a, n);
return 0;
}
那么我们来分析一下,这样做的复杂度是多少呢?
注意到,这个循环会执行 n n n 次,因此复杂度是 O ( n ) O(n) O(n) 的。
很多时候, O ( n ) O(n) O(n) 的复杂度都是很不错的,但是这也要分情况来讨论。如果针对乘方运算我们采用这种 O ( n ) O(n) O(n) 复杂度的算法,在数据过大是很有可能会超时,这个时候我们就要想到采用更优的手段来计算乘方了。
快速幂
原理
根据初中数学知识,我们知道有:
a
n
×
a
m
=
a
n
+
m
a^n \times a^m = a^{n + m}
an×am=an+m
(
a
n
)
m
=
a
n
m
(a^n)^m = a^{nm}
(an)m=anm
根据这两条公式,我们不难把 a n a^n an 进行拆分。
我们可以采用二进制拆分法将 n n n 进行拆分。
设 n = 2 i + 2 j + … + 2 k n = 2^i + 2 ^ j + … + 2 ^ k n=2i+2j+…+2k
那么有:
a n = a 2 i + 2 j + … + 2 k a^n=a^{2^i + 2^j + … + 2^k} an=a2i+2j+…+2k
根据上面两条公式,这个式子可以继续转化:
a n = a 2 i × a 2 j × … × a 2 k a^n=a^{2^i} \times a^{2^j} \times … \times a^{2^k} an=a2i×a2j×…×a2k
注意到:式子最多只能拆分成 log 2 n \log_2 n log2n 项,而且可以递推得到从 a 2 0 a^{2^0} a20 到 a 2 i a^{2^i} a2i 的所有取值,易得时间复杂度为 O ( log n ) O(\log n) O(logn)
代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
int QuickPower(int a, int b)
{
int ans = 1;
int num = a;
while(b > 0)
{
if(b % 2 == 1)
ans *= num;
num *= num;
b /= 2;
}
return ans;
}
signed main()
{
int a, n;
cin >> a >> n;
cout << QuickPower(a, n);
return 0;
}
习题:洛谷 P1126 快速幂