线性代数
主要参考了 oi-wiki。
线性方程组
首先搞清楚线性方程组的本质。或者说是搞清楚矩阵的本质。
将其看作矩阵乘法:
换句话说,我们将一个向量通过线性变换到了另一个向量,这就是线性方程组的本质。
解方程的本质就是根据变换矩阵和终向量,推算出原向量。
其实不难想到:
所以其实相当于做一个矩阵求逆了。
虽然说我觉得你在求解线性方程组的时候要把这个矩阵当作一类特殊矩阵,不然你会很难受 /kk
通常我们在求解线性方程组时会使用这样一个增广矩阵:
其实就是把有值的地方提取出来。
我们要处理这个矩阵,其实就相当于通过一些满足线性方程组的变换来修改这个矩阵。详情见下。
初等行变换
这里只用到了小学二年级的知识。
先记录一些初等矩阵。
对角矩阵
即有且只有主对角线上所有元素非 \(0\) 的矩阵。
这个矩阵的作用是倍乘,你想象把它乘到一个矩阵上(左乘),恰好就是每一行的所有数乘上那一行的 \(k\) 值。所以又称之为倍乘矩阵。
体现到线性方程组上就是把一个线性方程的系数同时扩大,正确性显然。
对换矩阵
画的不好看,尽量理解一下。
其实就是单位矩阵后,对某两 \(i,j\),使第 \((i,i)\) 项和第 \((j,j)\) 项变成 \(0\),使 \((i,j)\) 和 \((j,i)\) 变成 \(1\).
这个矩阵左乘后的作用是交换 \(i,j\) 两行。
体现在线性方程组上就是交换两个线性方程的顺序,正确性显然。
倍加矩阵
这里没有画省略号,感性理解一下。
这个矩阵是单位矩阵的基础上第 \((i,j)\) 项变成 \(k\)。
左乘后的作用是将第 \(j\) 行的系数每一项都扩大 \(k\) 倍加入第 \(i\) 行。
体现在线性方程组上就是加减消元法,正确性显然。
有了上述三种矩阵,就可以对原增广矩阵求解了。
高斯消元法
上三角矩阵:\((i,j)\) 项有值当且仅当 \(i\le j\)。
高斯消元的目标是将原增广矩阵通过上述初等变换变成上三角矩阵。
对于每一行,我们找到它的主元。当然第 \(i\) 行的主元就是第 \(i\) 个未知数。
如果这一行里这个未知数系数为 \(0\) 的话就用将有系数的对换上来。这里我们直接找系数绝对值最大的,因为这样做误差最小。
然后通过倍加操作,把后面所有行的第 \(i\) 列的系数全部变为 \(0\),这一步就是在消元了。
不难发现,递归此操作后,我们就会得到原增广矩阵变换后的上三角矩阵。
只需要求出最后一个方程未知数的值,再回代即可。
高斯约旦消元法
在这个方法中,我们将增广矩阵变换成对角矩阵。
这一步只需要在消元那一步的同时向上消元即可。
比较推荐实现这种方法,感觉更符合人类直觉。
判断是否无解或无数解
无解和无数解本质上都是两个方程描述了同一个未知数,描述相反就是无解,描述相同就是无数解。
以高斯约旦消元法为例,当我们遇到一列不存在非 \(0\) 系数时跳过当前列的消元,执行完整个消元过程。
如果某个方程左边为 \(0\) 右边确不为 \(0\) 那就是无解。如果两个方程描述了同一个未知数,那就是无数解。
这里给出 P2455 [SDOI2006] 线性方程组 的代码:
/*
The Order...
Active...
Punishment!
*/
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
//#define int long long
/*
furina /se /se
yanami /se /se
yamada /se /se
sayaka /se /se
konata /se /se
蓝仙未未 /se /se
*/
namespace TYX_YNXK{
#define il inline
#define bl bool
#define vd void
#define ll long long
#define ull unsigned ll
#define db double
#define ldb long db
#define pii pair<int,int>
#define fi first
#define se second
#define MP make_pair
#define pb push_back
#define N 55
#define INF 0x3f3f3f3f3f3f3f3fll
#define DEBUG cerr<<"\tfurina begin:\n"
#define END cerr<<"\tyanami end.\n"
const db eps=1e-8;
il int sgn(db x){if(fabs(x)<eps)return 0;return x<0?-1:1;}
int n;db s[N][N];
signed main(){
cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=n+1;j++)cin>>s[i][j];
for(int i=1,j=1,r;i<=n&&j<=n;i++,j++){//第 i 行第 j 列的消元
r=i;for(int k=i+1;k<=n;k++)if(sgn(fabs(s[k][j])-fabs(s[r][j]))==1)r=k;//找到绝对值最大的行
if(sgn(fabs(s[r][j]))==0){--i;continue;}//为 0,跳过
if(i^r)for(int k=1;k<=n+1;k++)swap(s[r][k],s[i][k]);//对换目标行
for(int k=1;k<=n;k++)if(k^i)for(int p=n+1;p>=i;p--)s[k][p]-=s[k][j]/s[i][j]*s[i][p];//消元
}
bl flag1=0,flag2=0;
for(int i=1,d;i<=n;i++){
d=0;for(int j=1;j<=n;j++)d+=sgn(s[i][j])!=0;
if(!d){
if(sgn(s[i][n+1])!=0)flag1=1;
else flag2=1;
}
}
if(flag1)return cout<<"-1\n",0;
if(flag2)return cout<<"0\n",0;
for(int i=1;i<=n;i++){
cout<<"x"<<i<<"=";
db val=s[i][n+1]/s[i][i];
if(sgn(val)==0)cout<<"0.000\n";
else cout<<fixed<<setprecision(3)<<val<<'\n';
}
return 0;
}
}
signed main(){
// freopen("Hazuki.in","r",stdin);
// freopen("Hazuki.out","w",stdout);
TYX_YNXK::main();
return 0;
}/*
SATT is the world's best Dynamic Trees algorithm!
*/
矩阵求逆
只需要增广一个单位矩阵做高斯约旦消元即可。
线性空间
就是由向量运算组成的域。性质都可以参照域的性质。
线性基
极大线性无关向量组。
异或线性基
维度为 \(base\),每一维只有 \(01\) 表示。
实现时从高到低遍历,如果某一位没有就加入,有就异或。

浙公网安备 33010602011771号