高斯消元
注:文章内容摘选自其他博客
模板代码来自: 作者:Alex_McAvoy 文章链接:线性代数——高斯消元法
一、线性方程组的求解
在求解线性方程组往往将其系数矩阵(或增广矩阵)化为上三角矩阵。
在化为上三角矩阵后,若增广矩阵中出现形如 0 0 0 0 a 的行,则该线性方程组无解;若增广矩阵中每一行均有非0元素(即上三角矩阵完整)则有唯一解;若含有多个全零行,则说明矩阵中含有自由元,计算矩阵的秩r,自由元的个数就为 n-r。
求解线性方程组(化为上三角矩阵)的步骤:(高斯消元法)
1.消元
2.回代
3.判断解的个数(无解、1个、无穷个)
二、线性方程组整数类型
int a[N][N];//增广矩阵
int x[N];//解集
bool freeX[N];//标记是否为自由变元
int GCD(int a,int b){
return !b?a:GCD(b,a%b);
}
int LCM(int a,int b){
return a/GCD(a,b)*b;
}
int Gauss(int equ,int var){
//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=true;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){
//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){
//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){
//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){
//col列第row行以下全是0,处理当前行的下一列
row--;
continue;
}
for(int i=row+1;i<equ;i++){
//枚举要删去的行
if(a[i][col]!=0){
int lcm=LCM(abs(a[i][col]),abs(a[row][col]));
int ta=lcm/abs(a[i][col]);
int tb=lcm/abs(a[row][col]);
if(a[i][col]*a[row][col]<0)//异号情况相加
tb=-tb;
for(int j=col;j<var+1;j++) {
a[i][j]=a[i][j]*ta-a[row][j]*tb;
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if (a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){
//计算解集
int temp=a[i][var];
for(int j=i+1;j<var;j++){
if(a[i][j]!=0)
temp-=a[i][j]*x[j];
}
if(temp%a[i][i]!=0)//有浮点数解,无整数解
return -2;
x[i]=temp/a[i][i];
}
return 0;
}
int main(){
int equ,var;//equ个方程,var个变元
while(scanf("%d%d",&equ,&var)!=EOF) {
memset(a,0,sizeof(a));
for(int i=0;i<equ;i++)//输入增广矩阵
for(int j=0;j<var+1;j++)
scanf("%d",&a[i][j]);
int freeNum=Gauss(equ,var);//自由元个数
if(freeNum==-1)//无解
printf("无解\n");
else if(freeNum==-2)//有浮点数解无整数解
printf("无整数解\n");
else if(freeNum>0){
//有无穷多解
printf("有无穷多解,自由变元个数为%d\n",freeNum);
for(int i=0;i<var;i++){
if(freeX[i])
printf("x%d是自由变元\n",i+1);
else
printf("x%d=%d\n",i+1,x[i]);
}
}
else{
//有唯一解
for(int i=0;i<var;i++)
printf("x%d=%d\n",i+1,x[i]);
}
printf("\n");
}
return 0;
}
三、线性方程组浮点数类型
double a[N][N];//增广矩阵
double x[N];//解集
bool freeX[N];//标记是否为自由变元
int Gauss(int equ,int var){
//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=true;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){
//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){
//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){
//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(fabs(a[row][col])<1e6){
//col列第row行以下全是0,处理当前行的下一列
row--;
continue;
}
for(int i=row+1;i<equ;i++){
//枚举要删去的行
if(fabs(a[i][col])>1e6){
double temp=a[i][col]/a[row][col];
for(int j=col;j<var+1;j++)
a[i][j]-=a[row][j]*temp;
a[i][col]=0;
}
}
}
/*求解*/
//无解
for(int i=row;i<equ;i++)
if(fabs(a[i][col])>1e6)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){
//计算解集
double temp=a[i][var];
for(int j=i+1;j<var;j++)
temp-=a[i][j]*x[j];
x[i]=temp/a[i][i];
}
return 0;
}
四、求解模线性方程组
问题:什么是模线性方程组?
举个例子:(可能不成立,胡编的)
x%3 = 2
x%5 = 1
x%2 = 1
就是一组线性方程组,在求解时,利用函数的思想,借助扩展欧几里得求解
ps:图片来自:模线性方程组
int a[N][N];//增广矩阵
int x[N];//解集
bool freeX[N];//标记是否为自由变元
int GCD(int a,int b){
return !b?a:GCD(b,a%b);
}
int LCM(int a,int b){
return a/GCD(a,b)*b;
}
int Gauss(int equ,int var){
//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=true;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){
//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){
//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){
//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){
//col列第row行以下全是0,处理当前行的下一列
row--;
continue;
}
for(int i=row+1;i<equ;i++){
//枚举要删去的行
if(a[i][col]!=0){
int lcm=LCM(abs(a[i][col]),abs(a[row][col]));
int ta=lcm/abs(a[i][col]);
int tb=lcm/abs(a[row][col]);
if(a[i][col]*a[row][col]<0)//异号情况相加
tb=-tb;
for(int j=col;j<var+1;j++) {
a[i][j]=((a[i][j]*ta-a[row][j]*tb)%MOD+MOD)%MOD;
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if (a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){
//计算解集
int temp=a[i][var];
for(int j=i+1;j<var;j++){
if(a[i][j]!=0)
temp-=a[i][j]*x[j];
temp=(temp%MOD+MOD)%MOD;//取模
}
while(temp%a[i][i]!=0)//外层每次循环都是求a[i][i],它是每个方程中唯一一个未知的变量
temp+=MOD;//a[i][i]必须为整数,加上周期MOD
x[i]=(temp/a[i][i])%MOD;//取模
}
return 0;
}
五、求解异或线性方程组
和线性加减方程组类似,只不过在化简上三角行列式的时候变为异或化简。
int a[N][N];//增广矩阵
int x[N];//解集
int freeX[N];//自由变元
int Gauss(int equ,int var){
//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=0;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int num=0;//自由变元的序号
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){
//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){
//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){
//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){
//col列第row行以下全是0,处理当前行的下一列
freeX[num++]=col;//记录自由变元
row--;
continue;
}
for(int i=row+1;i<equ;i++){
if(a[i][col]!=0){
for(int j=col;j<var+1;j++){
//对于下面出现该列中有1的行,需要把1消掉
a[i][j]^=a[row][j];
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if(a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){
//计算解集
x[i]=a[i][var];
for(int j=i+1;j<var;j++)
x[i]^=(a[i][j]&&x[j]);
}
return 0;
}
int enumFreeX(int freeNum,int var){
//枚举自由元,统计有解情况下1最少的个数
int sta=(1<<(freeNum));//自由元的状态总数
int res=INF;
for(int i=0;i<sta;++i){
//枚举状态
int cnt=0;
for(int j=0;j<freeNum;j++){
//枚举自由元
if(i&(1<<j)){
cnt++;
x[freeX[j]]=1;
}else
x[freeX[j]]=0;
}
for(int k=var-freeNum-1;k>=0;k--){
//没有自由元的最下面一行
int index=0;
for(index=k;k<var;index++){
//在当前行找到第一个非0自由元
if(a[k][index])
break;
}
x[index]=a[k][var];
for(int j=index+1;j<var;++j){
//向后依次计算出结果
if(a[k][j])
x[index]^=x[j];
}
cnt+=x[index];//若结果为1,则进行统计
}
res=min(res,cnt);
}
return res;
}
int main(){
memset(a,0,sizeof(a));
int equ,var;
scanf("%d%d",&equ,&var);
for(int i=0;i<equ;i++){
//构造初始状态
}
for(int i=0;i<equ;i++)//最终状态
scanf("%d",&a[i][var]);
int freeNum=Gauss(equ,var);//获取自由元
if(freeNum==-1)//无解
printf("inf\n");
else if(freeNum==0){
//唯一解
int res=0;
for(int i=0;i<var;i++)
res+=x[i];
printf("%d\n",res);
}
else{
//多个解
int res=enumFreeX(freeNum,var);
printf("%d\n",res);
}
return 0;
}
六、Matrix Equation
通过观察发现C矩阵列无关性.通过高斯消元统计每列的自由元的个数即可.
ps(也可以发现答案的解都为2的次幂)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e2 + 7;
const int mod = 998244353;
int a[maxn][maxn];//增广矩阵
int x[maxn];//解集
int freeX[maxn];//自由变元
int n;
int A[maxn][maxn],B[maxn][maxn];
ll poww(ll a,ll b){
ll ans=1;
while(b > 0){
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int Gauss(int equ,int var){
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=0;
}
int col=0;//当前处理的列
int num=0;//自由变元的序号
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){
//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){
//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){
//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){
//col列第row行以下全是0,处理当前行的下一列
freeX[num++]=col;//记录自由变元
row--;
continue;
}
for(int i=row+1;i<equ;i++){
if(a[i][col]!=0){
for(int j=col;j<var+1;j++){
//对于下面出现该列中有1的行,需要把1消掉
a[i][j]^=a[row][j];
}
}
}
}
for(int i=row;i<equ;i++)
if(a[i][col]!=0)
return -1;
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
return 0;
}
int main(){
scanf("%d",&n);
for(int i = 0; i < n; i ++){
for(int j = 0; j < n; j ++){
scanf("%d",&A[i][j]);
}
}
for(int i = 0; i < n; i ++){
for(int j = 0; j < n; j ++){
scanf("%d",&B[i][j]);
}
}
ll ans = 1;
for(int r = 0;r < n; r ++){
for(int i = 0;i < n;i++) {
for(int j = 0;j < n;j++) {
a[i][j] = A[i][j];
}
}
for(int i = 0; i < n; i ++){
a[i][i] = (A[i][i] - B[i][r] + 2) % 2;
// a[i][i] = (A[i][i] * B[i][r]);
}
int freeNum=Gauss(n,n);
if(freeNum == -1) continue;
ans = (ans * poww(2 , freeNum)) % mod;
}
printf ("%lld\n",ans);
return 0;
}