线性代数基础 \(Linear Algebra Basics\)
向量
向量定义
一个 \(n\) 维向量 \(V\) 可以表示为:
\V = (v_1, v_2, \\dots, v_n) \\
点积运算
两个 \(n\) 维向量 \(p\) 和 \(q\) 的点积:
\p \\cdot q = \\sum_{i=1}\^n p_i q_i \\
示例:
\\\begin{aligned} p \&= (1, 2, 3) \\\\ q \&= (4, 5, 6) \\\\ p \\cdot q \&= 1 \\times 4 + 2 \\times 5 + 3 \\times 6 = 32 \\end{aligned} \\
矩阵
矩阵定义
一个 \(m \times n\) 的矩阵 \(A\):
\A = \\begin{pmatrix} a_{11} \& a_{12} \& \\cdots \& a_{1n} \\\\ a_{21} \& a_{22} \& \\cdots \& a_{2n} \\\\ \\vdots \& \\vdots \& \\ddots \& \\vdots \\\\ a_{m1} \& a_{m2} \& \\cdots \& a_{mn} \\end{pmatrix} \\
特殊矩阵
单位矩阵 \(I_n\):
\I_n = \\begin{pmatrix} 1 \& 0 \& \\cdots \& 0 \\\\ 0 \& 1 \& \\cdots \& 0 \\\\ \\vdots \& \\vdots \& \\ddots \& \\vdots \\\\ 0 \& 0 \& \\cdots \& 1 \\end{pmatrix} \\
矩阵运算
矩阵加法
\(A + B)_{ij} = A_{ij} + B_{ij} \\
矩阵数乘
\(kA)_{ij} = k \\cdot A_{ij} \\
矩阵乘法
对于 \(A_{m \times n}\) 和 \(B_{n \times p}\):
\(AB)_{ij} = \\sum_{k=1}\^n A_{ik} B_{kj} \\
注意 : 矩阵乘法不满足交换律 !!!
再说一遍 :矩阵乘法不满足交换律 !!!
最后一遍 :矩阵乘法不满足交换律 !!!
示例:
\\\begin{pmatrix} 1 \& 2 \\\\ 3 \& 4 \\end{pmatrix} \\begin{pmatrix} 5 \& 6 \\\\ 7 \& 8 \\end{pmatrix} = \\begin{pmatrix} 1 \\times 5 + 2 \\times 7 \& 1 \\times 6 + 2 \\times 8 \\\\ 3 \\times 5 + 4 \\times 7 \& 3 \\times 6 + 4 \\times 8 \\end{pmatrix} = \\begin{pmatrix} 19 \& 22 \\\\ 43 \& 50 \\end{pmatrix} \\
代码模板
c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 500 + 5;
const int MOD = 1e9 + 7;
int n, p, m;
int A[N][N], B[N][N], C[N][N];
void read() {
cin >> n >> p >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= p; j++) {
cin >> A[i][j];
A[i][j] = (A[i][j] % MOD + MOD) % MOD;
}
}
for (int i = 1; i <= p; i++) {
for (int j = 1; j <= m; j++) {
cin >> B[i][j];
B[i][j] = (B[i][j] % MOD + MOD) % MOD;
}
}
}
void mul() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
ll sum = 0;
for (int k = 1; k <= p; k++) {
sum += (1ll * A[i][k] * B[k][j]) % MOD;
sum %= MOD;
}
C[i][j] = sum;
}
}
}
void print() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cout << C[i][j] << " ";
}
cout << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
read();
mul();
print();
return 0;
}
矩阵转置
\(A\^T)_{ij} = A_{ji} \\
矩阵快速幂
定义
对于方阵 \(A\):
\A\^k = \\underbrace{A \\times A \\times \\cdots \\times A}_{k \\text{次}} \\
快速算法
利用二进制分解:
\A\^k = \\prod_{i=0}\^{\\lfloor \\log_2 k \\rfloor} A\^{2\^i} \\
代码模板
c++
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 3;
const int MOD = 1e9 + 7;
struct Matrix {
int n;
int mat[N][N];
Matrix(int _n = 0) : n(_n) { memset(mat, 0, sizeof(mat)); }
void E() { for (int i = 1; i <= n; i ++) mat[i][i] = 1; }
};
Matrix mul(Matrix a, Matrix b) {
int n = a.n;
Matrix ret(n);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
for (int k = 1; k <= n; k ++) {
ret.mat[i][j] = (ret.mat[i][j] + 1ll * a.mat[i][k] * b.mat[k][j]) % MOD;
}
}
}
return ret;
}
Matrix quick_pow(Matrix x, long long p) {
int n = x.n;
Matrix ret(n);
ret.E();
while (p) {
if (p & 1) ret = mul(ret, x);
x = mul(x, x);
p >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
long long k;
cin >> n >> k;
Matrix a(n);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
cin >> a.mat[i][j];
a.mat[i][j] = (a.mat[i][j] % MOD + MOD) % MOD;
}
}
Matrix res = quick_pow(a, k);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
cout << res.mat[i][j] << ' ';
}
cout << '\n';
}
return 0;
}
常系数齐次线性递推与矩阵加速 !!!
一、线性递推的数学本质
1. 递推关系的矩阵表示
对于k阶线性递推关系:
\f_n = \\sum_{i=1}\^k c_i f_{n-i} \\quad (n \> k) \\
存在转移矩阵 \(A\) 满足:
\\\underbrace{ \\begin{bmatrix} f_n \\\\ f_{n-1} \\\\ \\vdots \\\\ f_{n-k+1} \\end{bmatrix} }_{V_n} = A \\times \\underbrace{ \\begin{bmatrix} f_{n-1} \\\\ f_{n-2} \\\\ \\vdots \\\\ f_{n-k} \\end{bmatrix} }_{V_{n-1}} \\
2. 转移矩阵的结构特性
矩阵 \(A\) 具有如下标准形式:! ! !
\A = \\begin{bmatrix} c_1 \& c_2 \& \\cdots \& c_{k-1} \& c_k \\\\ 1 \& 0 \& \\cdots \& 0 \& 0 \\\\ 0 \& 1 \& \\cdots \& 0 \& 0 \\\\ \\vdots \& \\vdots \& \\ddots \& \\vdots \& \\vdots \\\\ 0 \& 0 \& \\cdots \& 1 \& 0 \\end{bmatrix}_{k \\times k} \\
构造原理:
- 第一行:直接对应递推系数
- 次对角线:全1元素用于"移位"记录历史状态
二、矩阵加速的严格推导
1. 递推展开过程
通过递归展开可得:
\V_n = A \\times V_{n-1} = A\^2 \\times V_{n-2} = \\cdots = A\^{n-k} \\times V_k \\
其中初始向量:
\V_k = \\begin{bmatrix} f_k \\\\ f_{k-1} \\\\ \\vdots \\\\ f_1 \\end{bmatrix} \\
2. 时间复杂度分析
计算过程分为两步:
- 计算矩阵幂 \(A^{n-k}\) :
- 使用快速幂算法
- 每次矩阵乘法 \(O(k^3)\) 时间
- 总时间 \(O(k^3 \log n)\)
- 矩阵-向量乘法 :
- \(O(k^2)\) 时间
三、推广到非齐次情况
1. 含常数项的递推
对于递推式:
\f_n = \\sum_{i=1}\^k c_i f_{n-i} + d \\
构造技巧:
-
升维处理:
\\\widetilde{V}_n = \\begin{bmatrix} V_n \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} f_n \\\\ \\vdots \\\\ f_{n-k+1} \\\\ 1 \\end{bmatrix} \\
-
扩展转移矩阵:
\\\widetilde{A} = \\begin{bmatrix} A \& \\begin{matrix} d \\\\ 0 \\\\ \\vdots \\\\ 0 \\end{matrix} \\\\ 0 \\cdots 0 \& 1 \\end{bmatrix} \\
2. 含多项式项的递推
对于递推式:
\f_n = \\sum_{i=1}\^k c_i f_{n-i} + P(m) \\
其中 \(P(m)\) 是 \(m\) 次多项式
构造方法:
- 需要跟踪的变量:
- \(f_n\) 及其历史项
- \(m^j \ (j=0,1,...,m)\)
- 转移矩阵维度:
- 总维度 \(= k + (m + 1)\)
四、典型例题的解析理论
例题1:三阶递推问题
给定:
\f_n = f_{n-1} + f_{n-3} \\quad (n \\geq 4) \\
初始条件:\(f_1=f_2=f_3=1\)
矩阵构造过程
确定状态向量:
\V_n = \\begin{bmatrix} f_n \\\\ f_{n-1} \\\\ f_{n-2} \\end{bmatrix} \\
建立方程关系:
\\\begin{cases} f_n = 1·f_{n-1} + 0·f_{n-2} + 1·f_{n-3} \\\\ f_{n-1} = 1·f_{n-1} + 0·f_{n-2} + 0·f_{n-3} \\\\ f_{n-2} = 0·f_{n-1} + 1·f_{n-2} + 0·f_{n-3} \\end{cases} \\
得到转移矩阵:
\A = \\begin{bmatrix} 1 \& 0 \& 1 \\\\ 1 \& 0 \& 0 \\\\ 0 \& 1 \& 0 \\end{bmatrix} \\
求解步骤
计算幂次:
\power = n - 3 \\
最终表达式:
\\\begin{bmatrix} f_n \\\\ f_{n-1} \\\\ f_{n-2} \\end{bmatrix} = A\^{n-3} \\times \\begin{bmatrix} f_3 \\\\ f_2 \\\\ f_1 \\end{bmatrix} \\
五、理论边界与限制
1. 适用条件
- 必须严格满足线性关系
- 系数必须为常数(不能是 \(n\) 的函数)
2. 不适用情况
-
非线性递推:
\f_n = f_{n-1} \\times f_{n-2} \\
-
变系数递推:
\f_n = n \\cdot f_{n-1} \\
3. 维度爆炸问题
当递推式中包含:
- 多个多项式项
- 高次多项式时
会导致转移矩阵维度急剧增加,实际应用中需权衡:
\\\text{矩阵维度} = k + \\sum_{i=1}\^m (\\deg(P_i) + 1) \\
六、理论延伸
1. 与特征根法的联系
矩阵特征多项式:
\\\det(\\lambda I - A) = 0 \\
正是递推关系的特征方程,二者通解形式一致。
2. 广义矩阵加速
对于分块矩阵情形,可处理:
- 多个相互关联的递推式
- 二维递推问题
- 带有模运算的特殊递推
代码模板
c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;
const int N = 5;
struct Matrix {
int n;
int mat[N][N];
Matrix(int _n = 0) : n(_n) { memset(mat, 0, sizeof(mat)); }
void E() { for (int i = 1; i <= n; i++) mat[i][i] = 1; }
};
Matrix A(3), init(3);
void pre() {
A.mat[1][1] = 1;
A.mat[1][3] = 1;
A.mat[2][1] = 1;
A.mat[3][2] = 1;
init.mat[1][1] = 1;
init.mat[2][1] = 1;
init.mat[3][1] = 1;
}
Matrix mul(Matrix a, Matrix b) {
int n = a.n;
Matrix ret(n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= n; k++) {
ret.mat[i][j] = (ret.mat[i][j] + 1ll * a.mat[i][k] * b.mat[k][j]) % MOD;
}
}
}
return ret;
}
Matrix quick_pow(Matrix x, ll p) {
int n = x.n;
Matrix ret(n);
ret.E();
while (p) {
if (p & 1) ret = mul(ret, x);
x = mul(x, x);
p >>= 1;
}
return ret;
}
void solve(int n) {
if (n <= 3) {
cout << "1\n";
return;
}
Matrix ret = quick_pow(A, n - 3);
Matrix ans = mul(ret, init);
cout << ans.mat[1][1] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T; cin >> T;
pre();
while (T--) {
int n; cin >> n;
solve(n);
}
return 0;
}
线性方程组
增广矩阵表示
方程组:
\\\begin{cases} a_{11}x_1 + a_{12}x_2 + \\cdots + a_{1n}x_n = b_1 \\\\ a_{21}x_1 + a_{22}x_2 + \\cdots + a_{2n}x_n = b_2 \\\\ \\vdots \\\\ a_{m1}x_1 + a_{m2}x_2 + \\cdots + a_{mn}x_n = b_m \\end{cases} \\
增广矩阵:
\\\left( \\begin{array}{cccc\|c} a_{11} \& a_{12} \& \\cdots \& a_{1n} \& b_1 \\\\ a_{21} \& a_{22} \& \\cdots \& a_{2n} \& b_2 \\\\ \\vdots \& \\vdots \& \\ddots \& \\vdots \& \\vdots \\\\ a_{m1} \& a_{m2} \& \\cdots \& a_{mn} \& b_m \\end{array} \\right) \\
高斯消元法
基本操作
- 交换两行
- 某行乘以非零常数
- 将某行的倍数加到另一行
消元步骤
- 化为上三角矩阵
- 回代求解
示例:
\\\begin{aligned} \&\\left( \\begin{array}{ccc\|c} 1 \& 1 \& 1 \& 6 \\\\ 2 \& 1 \& 1 \& 10 \\\\ 1 \& 2 \& 1 \& 10 \\end{array} \\right) \\\\ \\rightarrow \&\\left( \\begin{array}{ccc\|c} 1 \& 1 \& 1 \& 6 \\\\ 0 \& -1 \& -1 \& -2 \\\\ 0 \& 1 \& 0 \& 4 \\end{array} \\right) \\\\ \\rightarrow \&\\left( \\begin{array}{ccc\|c} 1 \& 0 \& 0 \& 1 \\\\ 0 \& 1 \& 0 \& 4 \\\\ 0 \& 0 \& 1 \& -2 \\end{array} \\right) \\end{aligned} \\
解得:
\x = 1, \\quad y = 4, \\quad z = -2 \\
代码模板
c++
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10;
int n;
double a[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n + 1; j ++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i ++) {
int p = i;
while (p <= n && a[p][i] == 0) p ++;
if (p == n + 1) {
cout << "No Solution" << '\n';
return 0;
}
for (int j = 1; j <= n + 1; j ++)
swap(a[i][j], a[p][j]);
double tmp = a[i][i];
for (int j = 1; j <= n + 1; j ++)
a[i][j] /= tmp;
for (int j = 1; j <= n; j ++) {
if (i == j) continue;
tmp = a[j][i];
for (int k = 1; k <= n + 1; k ++) {
a[j][k] -= a[i][k] * tmp;
}
}
}
cout << fixed << setprecision(2);
for (int i = 1; i <= n; i ++)
cout << a[i][n + 1] << '\n';
return 0;
}
部分由 AI 润色