高斯消元和线性基
高斯消元
理论
求解线性方程组,例如
将它转化为矩阵乘法
上面的三个矩阵还是太麻烦了,我们将他变为增广矩阵
然后我们定义初等行变换
1.交换两行
2.将同一行的元素同时乘 \(val\)
3.将第 \(i\) 行的元素加到第 \(j\) 行上面
不难发现初等行变换不会改变方程的解。然后就像我们常规解方程一样进行消元就可以了。
具体操作为,对于每一个未知数,我们取一个最大的 \(a\) ,然后用初等行变换将其他行对应的地方变成 \(0\) ,最后我们可以得到一个这样的矩阵
我们就可以很方便的进行求解了。
例题:洛谷P3389
#include <bits/stdc++.h>
#define db double
using namespace std;
db a[105][105];
int n;
int main(){
ios::sync_with_stdio(false);
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 maxhang=i;
for(int j=i+1;j<=n;j++)
if(abs(a[j][i])>=abs(a[maxhang][i]))
maxhang=j;
for(int j=1;j<=n+1;j++)
swap(a[maxhang][j],a[i][j]);
if(a[i][i]==0) {
cout<<"No Solution\n";
return 0;
}
for(int j=1;j<=n;j++){
if(j==i) continue;
db sb=a[j][i]*1.0/a[i][i];
for(int k=i;k<=n+1;k++)
a[j][k]-=a[i][k]*sb;
}
}
for(int i=1;i<=n;i++) {
cout<<fixed<<setprecision(2)<<a[i][n+1]/a[i][i]<<endl;
}
return 0;
}
线性空间
我们先来定义几个概念
向量:一行矩阵或者一列矩阵。线性空间:由一组向量能表示的向量的集合。
线性相关和线性无关:在一组向量中,存在一个向量能够被其他向量线性表示,那么我们称这一组向量是线性相关的。
线性空间的基底:一组线性无关的,数量最多的向量。注意:线性空间的基底不一定唯一。
线性空间的维度:一组基底的向量个数。
矩阵的秩:通过高斯消元化成最简阶梯矩阵后,非零行向量/列向量的个数。
注意在选最大行的时候按 \(cost\) 选。
异或空间
我们刚刚讨论的是针对加减法的,那异或又有什么性质呢?
异或空间:有一组整数可以通过异或得到的整数集合。
线性相关,线性无关,线性基的定义和上面相似。
那么我们怎么求异或空间的线性基呢?
高斯消元的方法当然是可行的,但是我们有更好的办法。
线性基
考虑一个整数能否对线性基做出贡献,即能够和其中的其他整数异或得到其它位置的 \(1\),那么我们就对于一个要加入的整数 \(x\) 进行如下操作。
令线性基的第 \(i\) 位是 \(bas_i\),那么从高位到低位,如果 \(x\) 的第 \(i\) 位是 \(1\),那么我们考虑 \(bas_i\),若 \(bas_i\) 为零,那么 \(bas_i=x\),否则将 \(x=x\bigoplus bas_i\)。
我们可以简单的认为这一系列的操作就是看 \(x\) 能否为线性基贡献一个独一无二的 \(1\)。
那么代码实现也很简单了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=55;
int n,basis[N];
void Ins(int val){
for(int i=60;i>=0;i--)
if(val&(1ll<<i)) {
if(basis[i]==0) {basis[i]=val; return;}
else val^=basis[i];
}
}
signed main(){
cin>>n;
for(int i=1,x;i<=n;i++) cin>>x,Ins(x);
return 0;
}
在这里多提一点,线性基是可以在 \(O(64)\) 的时间内合并的。

浙公网安备 33010602011771号