本文只做基础入门,了解矩阵快速幂的基础应用。进阶内容可以去看其他dalao的~~
矩阵乘法
矩阵乘法
设
A
=
(
a
i
j
)
是
一
个
m
×
x
矩
阵
,
B
=
(
b
i
j
)
是
一
个
s
×
n
矩
阵
,
那
么
规
定
矩
阵
A
和
矩
阵
B
的
乘
积
是
一
个
m
×
n
矩
阵
C
=
(
c
i
j
)
设A=(a_{ij})是一个m×x矩阵,B=(b_{ij})是一个s×n矩阵,那么规定矩阵A和矩阵B的乘积是一个m×n矩阵C=(c_{ij})
设A=(aij)是一个m×x矩阵,B=(bij)是一个s×n矩阵,那么规定矩阵A和矩阵B的乘积是一个m×n矩阵C=(cij)
( a 11 a 12 a 13 a 21 a 22 a 23 ) ( b 11 b 12 b 21 b 22 b 31 b 32 ) = ( a 11 b 11 + a 12 b 21 + a 13 b 31 a 11 b 12 + a 12 b 22 + a 13 b 32 a 21 b 11 + a 22 b 21 + a 23 b 31 a 21 b 12 + a 22 b 22 + a 23 b 32 ) \begin{gathered} \begin{pmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \end{pmatrix} \begin{pmatrix} b_{11} & b_{12} \\ b_{21} & b_{22} \\ b_{31} & b_{32} \end{pmatrix} = \begin{pmatrix} a_{11} b_{11} + a_{12} b_{21} + a_{13} b_{31} & a_{11} b_{12} + a_{12} b_{22} + a_{13} b_{32} \\ a_{21} b_{11} + a_{22} b_{21} + a_{23} b_{31} & a_{21} b_{12} + a_{22} b_{22} + a_{23} b_{32} \end{pmatrix} \end{gathered} (a11a21a12a22a13a23)⎝⎛b11b21b31b12b22b32⎠⎞=(a11b11+a12b21+a13b31a21b11+a22b21+a23b31a11b12+a12b22+a13b32a21b12+a22b22+a23b32)
必须注意: 只有当第一个矩阵(左矩阵)的列数等于第二个矩阵(右矩阵)的行数时,两个矩阵才能相乘。
在矩阵乘法中必须注意矩阵相乘的顺序。矩阵的乘法不满足交换律,即在一般情况下,AB≠BA。
矩阵的乘法不满足交换律,但是满足结合律,这是矩阵快速幂的关键。
矩阵快速幂
在计算一些有递推公式的问题时,我们可以通过矩阵乘法来计算。
例如斐波那契数列。
f
n
=
f
n
−
1
+
f
n
−
2
f_n = f_{n-1} + f_{n-2}
fn=fn−1+fn−2
我们可以构造出一个矩阵T
使得
(
f
n
−
1
f
n
)
T
=
(
f
n
f
n
+
1
)
\begin{gathered} \begin{pmatrix} f_{n-1} & f_n \end{pmatrix} \ T= \begin{pmatrix} f_{n} & f_{n + 1} \end{pmatrix} \end{gathered}
(fn−1fn) T=(fnfn+1)
T = ( 0 1 1 1 ) T=\begin{gathered} \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} \end{gathered} T=(0111)
当我们想求斐波那契数列的某一位时,就可以将矩阵乘上n个T
由于矩阵乘法满足结合律,我们计算时可以先利用快速幂的思想来计算矩阵T的乘积。
快速幂的简单理解
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2;
void mul(int c[], int a[], int b[][N])
{
int temp[N] = {0};
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
temp[i] += a[j] * b[j][i];
}
}
memcpy(c, temp, sizeof temp);
}
void mul(int c[][N], int a[][N], int b[][N])
{
int temp[N][N] = {0};
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
for (int k = 0; k < N; k++)
{
temp[i][j] += a[i][k] * b[k][j];
}
}
}
memcpy(c, temp, sizeof temp);
}
int main()
{
int f[2] = {1, 1};
int a[N][N] = {
{0, 1},
{1, 1}
};
int n;
cin >> n;
n--;
while (n)
{
if (n & 1) mul(f, f, a);
mul(a, a, a);
n >>= 1;
}
cout << f[0] << endl;
return 0;
}
下代码为求解斐波那契数列的前n项和
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 3;
int n, m;
void mul(int c[], int a[], int b[][N])
{
int temp[N] = {0};
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
temp[i] = (temp[i] + (ll) a[j] * b[j][i]) % m;
}
}
memcpy(c, temp, sizeof temp);
}
void mul(int c[][N], int a[][N], int b[][N])
{
int temp[N][N] = {0};
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
for (int k = 0; k < N; k++)
{
temp[i][j] = (temp[i][j] + (ll) a[i][k] * b[k][j]) % m;
}
}
}
memcpy(c, temp, sizeof temp);
}
int main()
{
int f[3] = {1, 1, 1};
int a[N][N] = {
{0, 1, 0},
{1, 1, 1},
{0, 0, 1}
};
cin >> n >> m;
n--;
while (n)
{
if (n & 1) mul(f, f, a);
mul(a, a, a);
n >>= 1;
}
cout << f[2] << endl;
return 0;
}