第5章 密集线性问题和分解 (本文由微软Copilot - Think Deeper辅助完成)
1. 向量空间
1.1. 向量空间示例
1.2. 向量积
1.2.1 点积
1.2.2 哈达马德积(舒尔积)
1.2.3 克罗内克积
2. 线性方程
2.1. 矩阵乘法的直觉
2.2. 解集
2.3. 欠定系统
2.4. 超定系统
2.5. 定解
2.6. 齐次与非齐次
3. 解线性方程
3.1. 使用奇异值分解(SVD)
3.2. 完全正交分解
3.3. 使用QR分解
3.4. 使用Cholesky分解
3.5. 高斯消元法(行简化)3.5.1. 前向消元
3.5.2. 前向和后向代换
3.5.3. 部分选主和全选主
3.5.4. 高斯消元法的数值稳定性
3.5.5. 高斯消元算法示例
3.5.6. 行阶梯形
3.5.7. 简化行阶梯形
3.5.8. 行阶梯形示例
3.5.9. 阶梯枢轴列
4. 矩阵分解
-
4.1. QR分解
4.1.1. 方阵QR分解
4.1.2. 矩形矩阵QR分解
4.2. 计算QR分解
4.3. 格拉姆-施密特正交化
4.4. Householder变换
4.5. QL,RQ和LQ分解
4.6. Cholesky分解LL*
4.7. 厄米特矩阵
4.8. 正定(半定)矩阵
4.9. LDL分解
4.10. 下三角上三角(LU)分解
4.11. 下三角对角上三角(LDU)分解
4.12. 特征值和特征向量
4.13. 计算特征值和特征向量
4.13.1 计算特征值和特征向量的示例
4.14. 矩阵的特征分解
4.15. 奇异值分解
4.15.1 奇异值分解的应用
4.15.1.1 伪逆
4.15.1.2 解齐次线性方程
4.15.1.3 值域,零空间和秩
4.15.1.4 最近的正交矩阵
5. 线性映射
6. 张量
7. 子空间
7.1. 行空间和列空间
8. 矩阵的值域
8.1. 行空间示例
9. 基
9.1. 计算列空间基的示例
9.2. 计算行空间基的示例
9.3. 基向量的变化
9.4. 向量的协变和逆变
9.5. 创建基集合
9.6. 基的变化
9.7. 向量场
9.8. 坐标系
9.8.1. 笛卡尔、极坐标、曲线坐标、圆柱坐标和球坐标
9.9. 坐标变换
9.10. 仿射和曲线变换
10. 矩阵的秩
10.1. 计算秩的结论
11. 列空间的维度
12. 零空间(核)
12.1. 计算零空间的示例
13. 零度
14. 秩-零度定理
15. 矩阵的行列式
16. 求矩阵的逆
17. 线性代数基本定理
18. 置换矩阵
19. 增广矩阵
1. 向量空间
向量空间是一个由向量组成的集合,配备了两个满足特定公理的运算:向量加法和标量乘法。在这个空间中,向量可以进行加法运算,也可以被标量(通常为实数或复数)所乘,以得到新的向量。
向量空间满足以下八个公理,其中 VV 是向量集合, FF 是数域(例如实数域 RR 或复数域 CC)。
1.1 向量空间的例子
为了更直观地理解向量空间,我们来看一些具体的例子。
#include <iostream>
#include <Eigen/Dense>
intmain(){
// 定义二维向量 v1 和 v2
Eigen::Vector2d v1(1.0,2.0);
Eigen::Vector2d v2(3.0,4.0);
// 向量加法
Eigen::Vector2d v_sum = v1 + v2;
// 标量乘法
double scalar =2.0;
Eigen::Vector2d v_scaled = scalar * v1;
// 输出结果
std::cout <<"向量 v1: \n"<< v1 <<"\n\n";
std::cout <<"向量 v2: \n"<< v2 <<"\n\n";
std::cout <<"v1 + v2 = \n"<< v_sum <<"\n\n";
std::cout <<"2 * v1 = \n"<< v_scaled <<"\n";
return0;
}
代码详解:
包含头文件:
#include <iostream> // 标准输入输出库 #include <Eigen/Dense> // Eigen 库,用于线性代数运算
定义主函数:
int main() { // ...代码... return 0; }
定义向量:
Eigen::Vector2d v1(1.0, 2.0); Eigen::Vector2d v2(3.0, 4.0);
Eigen::Vector2d
表示 2 维双精度(double)向量。
向量加法和标量乘法:
Eigen::Vector2d v_sum = v1 + v2; Eigen::Vector2d v_scaled = scalar * v1;
v_sum
是向量加法的结果。
v_scaled
是标量乘法的结果。
输出结果:
std::cout <<"向量 v1: \n"<< v1 <<"\n\n"; std::cout <<"向量 v2: \n"<< v2 <<"\n\n"; std::cout <<"v1 + v2 = \n"<< v_sum <<"\n\n"; std::cout <<"2 * v1 = \n"<< v_scaled <<"\n";
运行结果:
向量 v1:
1
2
向量 v2:
3
4
v1 + v2 =
4
6
2 * v1 =
2
4
1.2 向量积
向量积是指向量之间的乘法运算,根据运算方式的不同,结果可能是标量或新向量。
1.2.1 点积
点积(内积)是两个向量对应分量相乘后再求和的运算,结果为一个标量。
公式:
示例代码:
#include <iostream>
#include <Eigen/Dense>
intmain(){
// 定义三个维度的向量 u 和 v
Eigen::Vector3d u(1.0,2.0,3.0);
Eigen::Vector3d v(4.0,5.0,6.0);
// 计算点积
double dot = u.dot(v);
// 输出结果
std::cout <<"向量 u: \n"<< u <<"\n\n";
std::cout <<"向量 v: \n"<< v <<"\n\n";
std::cout <<"u ⋅ v = "<< dot <<"\n";
return0;
}
代码详解:
u.dot(v)
计算向量
u
和v
的点积。
运行结果:
向量 u:
1
2
3
向量 v:
4
5
6
u ⋅ v = 32
计算:
1.2.2 哈达玛积(舒尔积)
哈达玛积(Hadamard 积)是两个同维度向量对应元素相乘,得到新的向量。
示例代码:
#include <iostream>
#include <Eigen/Dense>
intmain(){
// 定义向量 s 和 t
Eigen::Vector2d s(1.0,2.0);
Eigen::Vector2d t(3.0,4.0);
// 计算哈达玛积
Eigen::Vector2d hadamard = s.array()* t.array();
// 输出结果
std::cout <<"向量 s: \n"<< s <<"\n\n";
std::cout <<"向量 t: \n"<< t <<"\n\n";
std::cout <<"s ⊙ t = \n"<< hadamard <<"\n";
return0;
}
代码详解:
s.array()
将向量视为数组,以便进行按元素的操作。
*
运算符用于数组的逐元素乘法。
运行结果:
向量 s:
1
2
向量 t:
3
4
s ⊙ t =
3
8
1.2.3 克罗内克积
克罗内克积(Kronecker 积)是矩阵之间的张量积,结果是一个更大的矩阵。
示例代码:
#include <iostream>
#include <Eigen/Dense>
intmain(){
// 定义矩阵 A 和 B
Eigen::Matrix2d A;
Eigen::Matrix2d B;
A <<1,2,
3,4;
B <<0,5,
6,7;
// 计算克罗内克积
Eigen::MatrixXd kron =Eigen::kroneckerProduct(A, B).eval();
// 输出结果
std::cout <<"矩阵 A: \n"<< A <<"\n\n";
std::cout <<"矩阵 B: \n"<< B <<"\n\n";
std::cout <<"A ⊗ B = \n"<< kron <<"\n";
return0;
}
代码详解:
Eigen::kroneckerProduct(A, B)
计算克罗内克积。
eval()
用于强制计算结果,以确保后续输出正确。
运行结果:
矩阵 A:
1 2
3 4
矩阵 B:
0 5
6 7
A ⊗ B =
0 5 0 10
6 7 12 14
0 15 0 20
18 21 24 28
总结
通过以上内容,我们深入了解了向量空间的定义和公理,同时探讨了向量空间的具体例子。我们还介绍了向量乘积的不同类型,包括点积、哈达玛积和克罗内克积,并通过代码示例演示了如何计算和应用这些运算。
延伸阅读:
- 线性映射与矩阵表示
:了解向量空间之间的线性映射及其矩阵表示。
- 基与维度
:学习向量空间的基的概念,以及如何确定向量空间的维度。
- 内积空间
:深入探讨具有内积结构的向量空间,即赋予向量空间长度和角度的概念。
向量空间是线性代数的核心概念,广泛应用于物理学、工程学、计算机科学等领域。理解向量空间及其运算,对于深入学习和应用线性代数至关重要。
2. 线性方程
解析:这意味着我们将系数、未知数和常数项分别组合成矩阵和向量的形式,利用矩阵运算的简洁性,方便求解和分析。这种表示方法在工程、物理、计算机科学(例如计算机图形学、机器学习中的回归问题)等领域尤为重要。
2.1 矩阵乘法的直观理解
矩阵乘法可以被视为向量的线性组合。具体来说,矩阵与向量相乘,相当于用向量的各个分量对矩阵的列进行加权,然后将结果相加。
2.2 解集的可能性
一个线性方程组可能存在以下三种情况之一:
- 无数个解
:当方程组具有自由度,未知数多于独立方程数量,且方程彼此不矛盾。
- 唯一解
:当方程数量等于未知数数量,且方程组满秩(矩阵 AA 可逆)。
- 无解
:当方程组存在矛盾,无法找到同时满足所有方程的解。
解集的情况取决于方程与未知数的数量以及方程之间的独立性。
2.3 欠定系统
当方程数量少于未知数数量时,称为欠定系统。在这种情况下:
如果方程不矛盾,通常存在无数个解(无限解)。
如果方程矛盾,则无解。
2.4 超定系统
当方程数量多于未知数数量时,称为超定系统。在这种情况下:
方程可能矛盾,导致无解。
若方程近似一致,可以通过最小二乘法找到一个近似解,使得误差平方和最小。
2.5 适定系统
当方程数量等于未知数数量时,称为适定系统。在这种情况下:
如果矩阵 AA 满秩(可逆),则存在唯一解。
如果矩阵 AA 不满秩,则可能存在无数个解或无解。
根据具体情况,可选择不同的求解方法,平衡速度与精度。
2.6 齐次与非齐次方程组
2.7 线性方程组的求解方法
为了具体展示线性方程组的求解过程,我们将使用 C++ 和 Eigen 库编写代码,并添加详细的注释。
示例代码:求解线性方程组
#include <iostream>
#include <Eigen/Dense>
intmain(){
// 定义一个 3x3 的矩阵 A,系数矩阵
Eigen::Matrix3f A;
A <<1,2,3, // 第一行元素:a_{11}, a_{12}, a_{13}
4,5,6, // 第二行元素:a_{21}, a_{22}, a_{23}
7,8,10;// 第三行元素:a_{31}, a_{32}, a_{33}
// 定义一个 3x1 的向量 b,常数项
Eigen::Vector3f b;
b <<6,15,25;// 常数项 b_1, b_2, b_3
// 输出矩阵 A 和向量 b
std::cout <<"系数矩阵 A:\n"<< A << std::endl;
std::cout <<"常数向量 b:\n"<< b << std::endl;
// 使用 PartialPivLU 分解求解 Ax = b
Eigen::Vector3f x = A.partialPivLu().solve(b);
// 输出解 x
std::cout <<"方程组的解 x:\n"<< x << std::endl;
return0;
}
代码详解:
包含必要的头文件:
#include <iostream> // 用于标准输入输出 #include <Eigen/Dense> // 包含 Eigen 库的密集矩阵相关功能
主函数:
int main() { // ... 代码 ... return 0; }
定义系数矩阵 A:
Eigen::Matrix3f A; A << 1, 2, 3, 4, 5, 6, 7, 8, 10;
Eigen::Matrix3f
表示 3x3 的浮点矩阵。
使用逗号初始化器
<<
按行填充矩阵元素。
定义常数向量 b:
Eigen::Vector3f b;
b << 6, 15, 25;
Eigen::Vector3f
表示 3x1 的浮点向量。
填充常数项。
输出矩阵 A 和向量 b:
std::cout << "系数矩阵 A:\n" << A << std::endl;
std::cout << "常数向量 b:\n" << b << std::endl;
使用
std::cout
输出矩阵和向量。
求解线性方程组:
Eigen::Vector3f x = A.partialPivLu().solve(b);
partialPivLu()
:对矩阵 A 进行部分主元 LU 分解,数值稳定性较好。
solve(b)
:求解方程组 Ax=b
。
输出解 x:
std::cout << "方程组的解 x:\n" << x << std::endl;
输出求得的未知数向量 x。
运行结果:
系数矩阵 A:
1 2 3
4 5 6
7 8 10
常数向量 b:
6
15
25
方程组的解 x:
1
1
1
2.8 不同求解方法的比较
在求解线性方程组时,根据系数矩阵的性质和精度要求,可以选择不同的方法:
直接求逆(不推荐):
Eigen::Vector3f x = A.inverse() * b;
- 缺点
:计算矩阵逆存在数值不稳定性和较高的计算代价。
- 建议
:除非矩阵非常小且条件数较好,否则不推荐直接求逆。
LU 分解:
Eigen::Vector3f x = A.lu().solve(b);
- 适用场景
:一般情况下,LU 分解速度快,适用于方阵。
QR 分解:
Eigen::Vector3f x = A.householderQr().solve(b);
- 适用场景
:适用于非方阵或接近奇异的矩阵。
最小二乘法(适用于超定方程组):
Eigen::VectorXf x = A.colPivHouseholderQr().solve(b);
- 适用场景
:当方程数量多于未知数数量,需要找到误差最小的近似解。
Cholesky 分解(适用于正定矩阵):
Eigen::Vector3f x = A.ldlt().solve(b);
- 适用场景
:系数矩阵对称且正定,计算效率高。
2.9 线性方程组的数值稳定性
在实际计算中,系数矩阵的条件数会影响求解结果的精度:
- 条件数大
(矩阵接近奇异):求解结果对输入数据和舍入误差非常敏感,可能产生较大误差。
- 条件数小
(矩阵较好):求解结果对误差不敏感,计算结果可靠。
建议:
- 避免直接求逆