递归快速幂
template<typename T>
T qpow(T a, T n)
{
if (n == 0)
return T(1);
else if (n % 2 == 1)
return qpow(a, n - 1) * a;
else
{
T temp = qpow(a, n / 2);
return temp * temp;
}
}
非递归快速幂
a
n
=
a
b
i
2
i
+
.
.
.
+
b
1
2
1
+
b
0
=
a
b
i
2
i
×
.
.
.
×
a
b
1
2
1
×
a
b
0
a^n=a^{b_i2^i+...+b_12^1+b_0}=a^{b_i2^i}\times...\times a^{b_12^1}\times a^{b_0}
an=abi2i+...+b121+b0=abi2i×...×ab121×ab0
因此用各项
a
b
i
2
i
=
{
a
2
i
,
如
果
b
i
=
1
1
,
如
果
b
i
=
0
a^{b_i2^i}=\left\{\begin{matrix} a^{2^i}& ,如果b_i=1\\ 1&, 如果b_i=0 \end{matrix}\right.
abi2i={a2i1,如果bi=1,如果bi=0的积来计算
a
n
a^n
an,也就是连续项
a
2
i
a^{2^i}
a2i的乘积,其中跳过了那些二进制位
b
i
b_i
bi为0的项。因为
a
2
i
=
(
a
2
i
−
1
)
2
a^{2^i}=(a^{2^{i-1}})^2
a2i=(a2i−1)2。所以我们可以利用二进制位串从右到左来计算
a
n
a^n
an。
template<typename T>
T RightLeftBinaryExponentiation(T a, vector<T> b)
// 输入:一个数字a和存放二进制位b_i,...,b_0的数组b(n)
// 这些位来自于非负整数n的二进制展开式,但是在存储时b_0存放在b[0],b_i存放在b[i]
// 输出:a^n的值
{
T term = a;
T product;
if(b[0] == 1) {
product = a;
}
else {
product = 1;
}
for(int i=1; i< b.size(); ++i) {
term = term * term;
if(b[i] == 1) {
product = product * term;
}
}
return product;
}
如果我们现在不输入位串数组,直接输入n,那么就可以得到非递归快速幂的写法。
template<typename T>
T qpow(T a, T n)
{
T term = a;
T product = 1;
while(n) {
if(n&1) { // 按位与运算,对应位置上有一个为0结果为0,两个都是1结果为1,n&1==1,说明n的二进制位的最低位为1
product = product * term;
}
term = term * term;
n >>= 1;
}
return product;
}
进一步因为我们只想获得最后的值,所以我们可以直接对a进行操作。
template<typename T>
T qpow(T a, T n)
{
T product = 1;
while(n) {
if(n&1) { // 按位与运算,对应位置上有一个为0结果为0,两个都是1结果为1,n&1==1,说明二进制位的最低位为1
product = product * a;
}
a = a * a;
n >>= 1;
}
return product;
}
应用:
用快速幂计算斐波那契数
斐波那契数列:
F
n
=
{
F
n
−
1
+
F
n
−
2
,
n
≥
2
,
n
∈
N
∗
0
,
n
=
0
1
,
n
=
1
F_n=\left\{\begin{matrix} F_{n-1}+F_{n-2}&,n\geq 2,n\in N^* \\ 0&,n=0\\ 1&,n=1 \end{matrix}\right.
Fn=⎩⎨⎧Fn−1+Fn−201,n≥2,n∈N∗,n=0,n=1
首先我们可以构建这样一个递推关系
(
1
1
1
0
)
(
F
n
F
n
−
1
)
=
(
F
n
+
F
n
−
1
F
n
)
=
(
F
n
+
1
F
n
)
\begin{pmatrix} 1&1 \\ 1& 0 \end{pmatrix}\begin{pmatrix} F_n \\ F_{n-1} \end{pmatrix}=\begin{pmatrix} F_n+F_{n-1} \\ F_{n} \end{pmatrix}=\begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix}
(1110)(FnFn−1)=(Fn+Fn−1Fn)=(Fn+1Fn)
所以:
(
F
n
+
1
F
n
)
=
(
1
1
1
0
)
n
(
F
1
F
0
)
\begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix}=\begin{pmatrix} 1&1 \\ 1& 0 \end{pmatrix}^n\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix}
(Fn+1Fn)=(1110)n(F1F0)
令
M
=
(
1
1
1
0
)
M=\begin{pmatrix} 1&1 \\ 1& 0 \end{pmatrix}
M=(1110),则
(
F
n
+
1
F
n
)
=
M
n
(
F
1
F
0
)
\begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix}=M^n\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix}
(Fn+1Fn)=Mn(F1F0),那么
(
F
n
F
n
−
1
)
=
M
n
−
1
(
F
1
F
0
)
\begin{pmatrix} F_{n} \\ F_{n-1} \end{pmatrix}=M^{n-1}\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix}
(FnFn−1)=Mn−1(F1F0)
我们可以利用快速幂来计算
M
n
−
1
M^{n-1}
Mn−1.
我们这里就处理n比较小的情形,如果涉及n较大的情形,要涉及到高精度的问题。
#include<vector>
#include<iostream>
using namespace std;
vector<vector<int>> matrix_multiply(vector<vector<int>>& a, vector<vector<int>>& b) {
vector<vector<int>> c{{0, 0}, {0, 0}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
vector<vector<int>> matrix_pow(vector<vector<int>>& a, int n) {
vector<vector<int>> ret{{1, 0}, {0, 1}};
while (n) {
if (n & 1) {
ret = matrix_multiply(ret, a);
}
n >>= 1;
a = matrix_multiply(a, a);
}
return ret;
}
int fib(int n) {
if (n < 2) {
return n;
}
vector<vector<int>> q{{1, 1}, {1, 0}};
vector<vector<int>> res = matrix_pow(q, n - 1);
return res[0][0];
}
int main() {
for (int i = 0; i < 10;++i) {
cout << fib(i) << endl;
}
system("pause");
return 0;
}