快速冪 & 矩陣快速冪
快速冪
今天介紹快速冪和矩陣快速冪,首先今天如果我們要計算\(3^{15}\)這個數字,我們可以用一個簡單的迴圈去實作。
1
2
3
4
5typedef long long ll;
ll num = 1;
for(int i = 0;i < 15;i++)
num *= 3;
cout << num << '\n';1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using namespace std;
typedef long long ll;
ll mod = 1000000007;
ll fast_pow(int base, int exp){
if(exp == 0) return 1;
ll res = 1;
if(exp & 1){
res = fast_pow(base, exp >> 1);
return res * res * base % mod;
}
res = fast_pow(base, exp >> 1);
return res * res % mod;
}
int main(){
IOS
int base = 3, exp = 15;
cout << fast_pow(base, exp) << '\n';
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;
typedef long long ll;
ll mod = 1000000007;
ll fast_pow(int base, int exp){
ll res = 1;
while(exp > 0){
if(exp & 1) res = res * base % mod;
base = base * base % mod;
exp >>= 1;
}
return res;
}
int main(){
IOS
int base = 3, exp = 15;
cout << fast_pow(base, exp) << '\n';
return 0;
}
矩陣快速冪
首先要懂得矩陣乘法,矩陣乘法如下
\[ A= \left[ \begin{matrix} a & b \\ c & d \end{matrix} \right] \tag{1}, B= \left[\begin{matrix} e & f \\ g & h \end{matrix}\right] \]
\[
A\times B =
\left[
\begin{matrix}
ae+bg & af+bh \\
ce+dg & cf+dh
\end{matrix}
\right]
\]
說白了就是前列後行做內積而已。再來我們要寫一個矩陣乘法的code,矩陣乘法的時間複雜度為\(O(N^3)\),這邊我們拿二階方陣當作示範。
1
2
3
4
5
6
7
8
9
10
11
12
13
14struct Matrix{
ll mat[2][2] = {{0}};
Matrix operator * (Matrix &inp){
Matrix tmp;
for(int i = 0;i < 2;i++){
for(int j = 0;j < 2;j++){
for(int k = 0;k < 2;k++){
tmp.mat[i][j] = ((tmp.mat[i][j] + (mat[i][k] % mod) * (inp.mat[k][j] % mod)) % mod) % mod;
}
}
}
return tmp;
}
};*
。 接著我們要來寫快速冪的部分了。 code:
1
2
3
4
5
6
7
8
9Matrix fast_pow(int n){
if(n == 1) return base;
if(n % 2 == 0){
Matrix res = fast_pow(n >> 1);
return res * res;
}
Matrix res = fast_pow(n >> 1);
return base * res * res;
}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
using namespace std;
typedef long long ll;
ll mod = 1000000007;
struct Matrix{
ll mat[2][2] = {{0}};
Matrix operator * (Matrix &inp){
Matrix tmp;
for(int i = 0;i < 2;i++){
for(int j = 0;j < 2;j++){
for(int k = 0;k < 2;k++){
tmp.mat[i][j] = ((tmp.mat[i][j] + (mat[i][k] % mod) * (inp.mat[k][j] % mod)) % mod) % mod;
}
}
}
return tmp;
}
};
Matrix base;
Matrix fast_pow(int exp){
if(exp == 1) return base;
if(exp % 2 == 0){
Matrix res = fast_pow(exp >> 1);
return res * res;
}
Matrix res = fast_pow(exp >> 1);
return base * res * res;
}
int main(){
IOS
base.mat[0][0] = 1;
base.mat[0][1] = 4;
base.mat[1][0] = 2;
base.mat[1][1] = 3;
Matrix output = fast_pow(10);
for(int i = 0;i < 2;i++){
for(int j = 0;j < 2;j++){
cout << output.mat[i][j] << ' ';
}
cout << '\n';
}
return 0;
}
$$$$
快速計算費氏數列
通常我們會使用遞迴或是迴圈的方式計算費氏數列,因為我們有一個\(f(i) = f(i - 1) + f(i - 2)\)的關係式,我們就可以推得費氏數列的第\(N\)項為多少。 然而我們可以使用矩陣乘法的方式得到費氏數列的第\(N\)項,並利用快速冪來增加運算速度。 首先我們先看這個矩陣 \[ A = \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right] \] 這一個矩陣是一個2階方陣,看似平平無奇的矩陣卻是我們今天的主角。先講結論,如果我們想要求費氏數列第\(n\)項,我們算出\(A\)矩陣的的\(n - 1\)次方,而此費氏數列的第\(n\)項就會是這個矩陣的第一個元素,也就是 \[ fib(n) = A^{n - 1}[0][0] \] \[ {\left[ \begin{matrix} 1 & 1\\ 1 & 0 \end{matrix} \right]}^{n-1}= \left[ \begin{matrix} fib(n) & ...\\ ... & ... \end{matrix} \right] \] 也就是左上方那個元素就是我們費氏數列第\(n\)項
$$$$
Proof by Mathematical induction
到現在我們有費氏數列的兩種計算方式,一個氏藉由遞迴關係式遞推而來,一個是藉由矩陣乘法來推得費氏數列第\(N\)項。 \[
A=
\left[
\begin{matrix}
1 & 1\\
1 & 0
\end{matrix}
\right] \tag{1}
\] \[
f(n)=\begin{cases} 1 &\mbox{if } n\mbox{=1 or 2} \\
fib(n - 1) + fib(n - 2) &\mbox{n } \geq \mbox{3}
\end{cases} \tag{2}
\] 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
using namespace std;
typedef long long ll;
ll mod = 1000000007;
struct Matrix{
ll mat[2][2] = {{0}};
Matrix operator * (Matrix &inp){
Matrix tmp;
for(int i = 0;i < 2;i++){
for(int j = 0;j < 2;j++){
for(int k = 0;k < 2;k++){
tmp.mat[i][j] = ((tmp.mat[i][j] + (mat[i][k] % mod) * (inp.mat[k][j] % mod)) % mod) % mod;
}
}
}
return tmp;
}
};
Matrix base;
Matrix fast_pow(int n){
if(n == 1) return base;
if(n % 2 == 0){
Matrix res = fast_pow(n >> 1);
return res * res;
}
Matrix res = fast_pow(n >> 1);
return base * res * res;
}
int main(){
IOS
base.mat[0][0] = 1;
base.mat[0][1] = 1;
base.mat[1][0] = 1;
base.mat[1][1] = 0;
Matrix output = fast_pow(20);
cout << output.mat[0][0] << '\n';
return 0;
}
$$$$
Python version
1 | import time |
1 | import time |