线性代数学习笔记(一):线性代数基础知识
向量
定义
从偏计算机的角度分析,这是排成一列的数。
从偏物理的角度分析,这是一条有方向有长度的线段。
可以通过数形结合的方式来理解向量。
向量的表示法:
虽然向量的起点不固定,但画平面直角坐标系中的向量,我们一般将向量的起点放在
这个向量可以表示成
模长:向量
零向量:模长为
单位向量:模长为
运算
基
其实,任何一个向量
观察到,平面中的所有向量都可以表示成
扩展而言,非共面的三个向量
张成空间
如果
矩阵
定义
由
单位矩阵(对角矩阵):一个
运算
-
加法:一个
的矩阵 ,加上一个 的矩阵 ,得到一个 的矩阵 ,其中 ; -
乘法:一个
的矩阵 ,乘以一个 的矩阵 ,得到一个 的矩阵 ,其中 。
其实矩阵乘法是一个很反直觉的事情,应为按理说矩阵乘法也应该像矩阵加法一样,对应位置相乘。但现实中的矩阵乘法确实
其实矩阵一开始是用来简化表示多元一次方程组的,比如以下二元一次方程方程组:
将它改写成矩阵形式:
此时是一个矩阵乘以一个向量的形式,可以初步感受到矩阵乘法的法则是对的。
下面考虑将
重新整理
将原式改写成矩阵形式:
可以发现,如果运用矩阵乘法,乘出来的矩阵再变为二元一次方程组的形式,会和整理后的方程一模一样,这也是为什么矩阵乘法要这样定义了。
- 转置:一个
的矩阵 ,转置成一个 的矩阵 ,其中
逆
对于一个矩阵
线性变换
线性变换是一个由一个向量映射到另一个向量的函数,满足:
-
原本终点同一条直线上的向量变换后终点仍在同一条直线上;
-
原来的零向量变换后还是零向量。
注意到,在线性变换中,一个向量的不同部分的缩放比是相同的,否则会将之前的直线变为曲线,比如离原点越远,放大比例就越大的变换:
这种变换会形成曲线,因此不是线性变换。
而剪切这种变换,将横着的线段不变化,竖着的线段顺时针倾斜
这种变换符合线性变换的性质,因此剪切变换是线性变换。
由于一个向量不同部分的缩放比相同,因此如果
行列式
我们讲到,在线性变换中,向量的模长与方向都发生了变化,那么平面上的所有正方形的面积都发生了变化,如果把正方形的大小变得无限接近于
记一次线性变换的面积变化为
其实对于二维空间,
来理解。接着你会发现中小学的新定义运算中的一类经典题其实就是在求二维矩阵的行列式,原来我小学就会线性代数了啊。
当
行列式有一个重要的性质:
列空间
我们知道一个线性变换可以通过将变换后的基拼在一起成为一个矩阵来表示,而这一组基张成的空间就成为这个矩阵的列空间。
这与之前介绍的矩阵与线性方程组的关系有所联系,如果等式右边的答案向量不在该系数矩阵的列空间中,那么这个线性方程组就是无解的,因为无论如何都找不到一个向量,在进行该线性变换后,可以得到答案向量。
秩
一个线性变换的列空间的维度,如果一个线性变换前的秩与变换后的秩没发生变化,那么该线性变换就未造成空间的坍缩,否则该线性变换就使空间的某些维坍缩了。
如果一个变换的秩是该变换能达到的最大的秩,那么就称该线性变换的矩阵是满秩的,比如一个
零空间
如果一个线性变换使空间的某一维坍缩了,那么会有一些向量被映射到了原点,这些向量组成的空间就是零空间。零空间可以用来表示一个线性方程组解的个数。
点积
定义两个向量
其实,点积有它的几何性质,如图:
两个向量的点积,等于一个向量
很显然,如果
下面我们来证明为什么是这样的。
考虑到两个向量的乘积变成了一个数字,其实是发生了空间的坍缩,而这恰好可以用矩阵来表示,那么现在就需要找到一个从二维空间到一维空间的线性变换。
首先,由于该线性变换的秩是
考虑到一维空间就是一条数轴,那么我们可以将数轴放上平面直角坐标系,记这条数轴的的基为
那么此时,平面上的任何一个点,往这条数轴作垂线,都可以得到一个数:
这时,我们其实已经发现这个变换了,现在考虑的就是如何将
现在来考虑变换后的
现在考虑模长不是
其实,点积是存在交换律的,可以计算证明,也可以通过射影定理证明,此处不再赘述。
叉积
定义向量
可以发现,由于行列式可正可负,因此叉积的值也可正可负,这取决于平面是否翻转。如果
事实上,狭义的叉积只对三维空间有用。当两个三维空间中的向量
我们先假设不知道如何计算两个三维向量的叉积,此时,通过二维空间两个向量的叉积合理外推,三维空间的叉积应该需要三个向量
这时我们把
从计算的角度思考,等式左边为
接着来求解叉积的几何性质。
首先,我们知道点积是将一个向量投影到另一个向量上,再乘以这个向量的模长。
现在,我们知道,
那么,这个平行六面体的体积就等于底面积与投影的积,而这正好等于
特征向量与特征值
定义一个线性变换
可以发现
以斐波那契数列为例。首先,斐波那契数列的递推公式为
假设特征向量
显然,当
接下来求解特征向量,将
同理,可以求出另一个特征向量
基变换
有些时候,我们用的基是
假设
当我们需要把别的基中的向量运用当前基的线性变换时,只需要乘上
这就是基变换的基础知识,它最直接的应用就是求递推函数的通项公式,下面以斐波那契数列为例。
求递推函数通项公式
我们知道斐波那契数列的转移矩阵是
我们已经知道了斐波那契数列的转移矩阵的特征向量是
变换
变换
将逆矩阵求出来,原式就变成了
由于
接着进行普通的矩阵乘法,那么原式
因此,
高斯消元法
初等矩阵
定义以下三种矩阵为初等矩阵:
- 倍乘矩阵:左上到右下对角线上第
个位置为 ,对角线上其它位置全为 ,非对角线位置全为 的矩阵:
它只有一个维度扩大了
- 对换矩阵:将单位矩阵第
排第 列的 交换到第 列,将第 排第 列的 交换到第 列 得到的矩阵:
这相当于是将两个向量对调了一下位置,此时“平面”发生了翻转,行列式为
- 倍加矩阵:将单位矩阵第
行第 列变为 所得到的矩阵:
此时会发现某个向量往一个方向平移了一段距离,但是“高”没有发生变化,因此行列式为
初等变换
对于一个矩阵,他的初等变换有三种:
-
用一个非
数 乘以矩阵的某一行或某一列,相当于这个矩阵乘以了倍乘矩阵; -
将矩阵某一行或某一列乘以非
数 ,并加到另一行或列,相当于这个矩阵乘以了倍加矩阵; -
交换矩阵的某两行或列,相当于这个矩阵乘以了对换矩阵。
这也就是平时我们在求解线性方程组的操作方程。
普通高斯消元法
首先,我们可以通过列空间判断是否有解,通过零空间判断有多少组解,这里只考虑有唯一解的情况。
由于我们知道矩阵与线性方程组有密不可分的关系,因此可以将线性方程组变化成矩阵形式:
其中
普通高斯消元的目标是将
具体做法是枚举每一列,将第 double
可以直接相除,再将主元
接着从下往上递推,由于最后一排一定是
完整代码:洛谷 P3389 【模板】高斯消元法
//懒得写了,我们一般都用高斯-约旦消元法
高斯-约旦消元法
此算法在普通高斯消元的基础上,从后往前将主元上方的数给消掉了,这样只用将
完整代码(来个双倍经验):洛谷 P2455 [SDOI2006] 线性方程组
#include <bits/stdc++.h>
using namespace std;
double a[105][105], eps = 1e-6;
int main(){
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n + 1; j++)
scanf("%lf", &a[i][j]);
for(int i = 1; i <= n; i++) {
int m = i;
for(int j = 1; j <= n; j++){
if(fabs(a[j][j]) > eps && j < i)
continue;
if(fabs(a[j][i]) > fabs(a[m][i]))
m = j;
}
for(int j = 1; j <= n + 1; j++)
swap(a[i][j], a[m][j]);
if(abs(a[i][i]) <= eps)
continue;
for(int j = 1; j <= n; j++)
if(j != i){
double d = a[j][i] / a[i][i];
for(int k = i; k <= n + 1; k++)
a[j][k] -= a[i][k] * d;
}
}
int key = 1;
for(int i = 1; i <= n; i++)
if(abs(a[i][i]) <= eps)
if(abs(a[i][n + 1]) > eps)
key = -1;
else if(key != -1)
key = 0;
if(key != 1){
printf("%d", key);
return 0;
}
for(int i = 1; i <= n; i++)
printf("x%d=%.2lf\n", i, a[i][n + 1] / a[i][i]);
return 0;
}
下面展示一下高斯消元两个经典应用。
行列式求值
行列式的变换
先来两个行列式特有的变换:
- 进行一次矩阵转置,行列式不变;
证明:感性理解。考虑到行列式的表达式在枚举列的排列,每列的每个数都与从另外那几列 每一列随机选出一个数相乘。由于对称性,枚举行依然会枚举到这种排列,两种枚举方式都枚举了所有排列,因此转置一次,行列式不变。
- 若矩阵有两行成比例,行列式为
;
证明:首先可以发现,将矩阵转置后,行列式不变,那么问题可以变成矩阵有两列成比例。
这就意味着这两个向量在同一条直线上,不能同时是这个空间的一组基,那么就只剩下
行列式也有初等变换,分别为:
- 用一个非
数 乘以矩阵的某一行或某一列,行列式也乘以 ;
证明:根据
- 将矩阵某一行或某一列乘以非
数 ,并加到另一行或列,行列式不变;
证明:倍加矩阵的行列式为
- 交换矩阵的某两行或列,行列式取反。
证明:对换矩阵的行列式为
代数余子式
先考虑以下
根据矩阵交换两行后,行列式取反,那么原式
根据行列式的计算式,每次要从每列选出行号互不相同的
矩阵也可以按照别的列进行展开,方法类似。
现在我们就有了余子式的定义。在一个
将
有强迫症的人可能旧的正负号很碍眼,于是我们又定义
此时这里的
考虑到矩阵转置后行列式不变,那么
行列式求值
考虑到矩阵某一行如果有
通过行列式初等变换变为以下矩阵:
那么该矩阵的行列式就可以表达为
对里面的行列式继续初等变换,那么最终答案就变成了
注意到洛谷上行列式求值的模板题为任意模数,这时对要消元的两行进行辗转相减法才可以消元。
完整代码:洛谷 P7112 【模板】行列式求值
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 6e2 + 9;
int a[N][N], n, p, ans = 1, neg = 1;
signed main(){
scanf("%lld%lld", &n, &p);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%lld", &a[i][j]);
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
while(a[i][i]){
int d = a[j][i] / a[i][i];
for(int k = i; k <= n; k++)
a[j][k] = ((a[j][k] - d * a[i][k]) % p + p) % p;
for(int k = 1; k <= n; k++)
swap(a[i][k], a[j][k]);
neg = -neg;
}
for(int k = 1; k <= n; k++)
swap(a[i][k], a[j][k]);
neg = -neg;
}
}
for(int i = 1; i <= n; i++)
ans = ans * a[i][i] % p;
ans *= neg;
printf("%lld\n", (ans + p) % p);
return 0;
}
矩阵求逆
首先,如果一个矩阵的行列式为
考虑可逆矩阵
将每一行展开,都可以得到一个线性方程组,套用高斯-约旦消元法的解法,如果我们能做到以下变换:
那么
完整代码:洛谷 P4783 【模板】矩阵求逆
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 409, MOD = 1e9 + 7;
int a[N][N << 1], n;
int extend_gcd(int a, int b, int &x, int &y){
if(b == 0){
x = 1;
y = 0;
return 0;
}
int d = extend_gcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int mod_inverse(int a, int m){
int x, y;
extend_gcd(a, m, x, y);
return (x % m + m) % m;
}
signed main(){
scanf("%lld", &n);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++)
scanf("%lld", &a[i][j]);
a[i][i + n] = 1;
}
for(int i = 1; i <= n; i++){
int maxx = i;
for(int j = i + 1; j <= n; j++)
if(a[j][i] > a[maxx][i])
maxx = j;
for(int j = 1; j <= 2 * n; j++)
swap(a[i][j], a[maxx][j]);
if(a[i][i] == 0){
printf("No Solution");
return 0;
}
int inv = mod_inverse(a[i][i], MOD);
for(int j = 1; j <= n; j++){
if(j != i){
int tmp = a[j][i] * inv % MOD;
for(int k = i; k <= n * 2; k++)
a[j][k] = ((a[j][k] - a[i][k] * tmp) % MOD + MOD) % MOD;
}
}
for(int j = 1; j <= n * 2; j++)
a[i][j] = a[i][j] * inv % MOD;
}
for(int i = 1; i <= n; i++){
for(int j = n + 1; j <= 2 * n; j++)
printf("%lld ", a[i][j]);
printf("\n");
}
return 0;
}
参考资料
-
程序员的数学3:线性代数 平冈和幸 掘玄
-
理解矩阵乘法 阮一峰
-
线性代数学习笔记(代数版) 自为风月马前卒
-
线性代数学习笔记(几何版) 自为风月马前卒
-
【官方双语/合集】线性代数的本质 - 系列合集 biliblii 3Blue1Brown
-
P3389 【模板】高斯消元法 皎月半洒花
-
数学(5)——线性代数:行列式 Reywmp
-
《线性代数》学习笔记 Alex_wei
本文来自博客园,作者:JPGOJCZX,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/18422810
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效