【数学·线性代数】线性方程组
2.2.线性方程组
2.2.1.初等矩阵变换
- 把某一行乘一个非零的数;
- 交换某两行;
- 把某行的若干倍加到另一行上去。
2.2.2.高斯消元解m×n线性方程组\(O(mn^2)\)
\(\begin{cases}a_{11}x_1+\cdots+a_{1n}x_n=b_1\\\vdots\\a_{m1}x_1+\cdots+a_{mn}x_n=b_m\end{cases}\Rightarrow [A,b]=A'_{m×(n+1)}\)
高斯消元化\(A'_{m×(n+1)}\)的A为行最简形,得到\(A''_{m×(n+1)}\):
h和l都从1开始。借助循环依次处理第1~n列。对于第l列:
-
在第h~m行中,找到第l列系数的绝对值最大的一行\(h_{max}\)。
如果该系数不为0,记录\(key_h=l\)。
否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列。
-
将第\(h_{max}\)行与第h行交换。(交换某两行)
-
将交换后的第h行的第一个有效数字(第l列)变成1。(把某一行乘一个非零的数)
-
将上下除第h行外的所有行的第l列清成0。(把某行的若干倍加到另一行上去)
-
执行循环底部的h++。
执行后如果h>m,跳出循环;否则执行l++处理完第l列。
r(A)=h-1。
判断解的个数:
- 有解 \(\Leftrightarrow\) r(A)==r([A,b]) \(\Leftrightarrow\) \(\forall i\in[r(A)+1,m]\cap\mathbb{Z},A''_{i,n+1}==0\)。
- 有唯一解 \(\Leftrightarrow\) r(A) == r([A,b]) == n。
- 否则有无穷多解。
- 否则无解。
求出一个合法解:
令\(x_{key_i}=A''_{i,n+1},i\in[1,r(A)]\cap\mathbb{Z}\),其余的即为自由变量\(x_j=0\)。
若要求出其他的合法解,则令自由变量\(x_j\)取任意常数,代入方程求出\(x_{key_i}\)。
//i:m;j:n
int m,n;
double a[M][N],x[N];
int ra,key[M];
int gauss()
{
//高斯消元化A'_{m×(n+1)}的A为行最简形,得到A''_{m×(n+1)}
int h,l;
for(h=1,l=1;l<=n;l++) //h和l都从1开始。借助循环依次处理第1~n列
{
//在第h~m行中,找到第l列系数的绝对值最大的一行h_max
int h_max=h;
for(int i=h+1;i<=m;i++)
if(dcmp(fabs(a[i][l]),fabs(a[h_max][l]))>0)
h_max=i;
if(dcmp(fabs(a[h_max][l]),0)>0) key[h]=l; //如果该系数不为0,记录key_h=l
else continue; //否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列
//将第h_max行与第h行交换。(交换某两行)
for(int j=l;j<=n+1;j++) swap(a[h][j],a[h_max][j]);
//将交换后的第h行的第一个有效数字(第l列)变成1。(把某一行乘一个非零的数)
for(int j=n+1;j>=l;j--) a[h][j]/=a[h][l];
//将上下除第h行外的所有行的第l列清成0。(把某行的若干倍加到另一行上去)
for(int i=1;i<=m;i++)
{
if(i==h) continue;
for(int j=n+1;j>=l;j--) //倒序是因为要用到a[i][l]被清成0之前的值,到l为止是因为a[1~l-1]已经被上面清成0了
a[i][j]-=a[h][j]*a[i][l]; //把某行的若干倍加到另一行上去,达到下面所有行的第l列清成0的目的
//思维:此时可以把外层循环i看作定值,且a[h][l]=1
}
//执行循环底部的h++
h++;
if(h>m) break; //执行后如果h>m,跳出循环;否则执行l++处理完第l列
}
ra=h-1;
//判断解的个数
for(int i=ra+1;i<=m;i++)
if(dcmp(a[i][n+1],0)!=0)
return 0;
if(ra!=n) return 2;
return 1;
}
int cnt=gauss();
if(cnt==0) puts("No solution");
else
{
if(cnt==1) puts("Unique solution");
else puts("Infinite group solutions");
//求出一个合法解
for(int j=1;j<=n;j++) x[j]=0;
for(int i=1;i<=ra;i++) x[key[i]]=a[i][n+1];
for(int j=1;j<=n;j++) printf("%.2lf\n",x[j]);
}
2.2.3.高斯消元解m×n线性异或方程组\(O(mn^2)\)
\(\begin{cases}a_{11}x_1\operatorname{xor}\cdots\operatorname{xor}a_{1n}x_n=b_1\\\vdots\\a_{m1}x_1\operatorname{xor}\cdots\operatorname{xor}a_{mn}x_n=b_m\end{cases}\Rightarrow[A,b]=A'_{m×(n+1)}\\a_{ij}\in\{0,1\}\)
特别地,\(\begin{cases}(a_{11}\operatorname{and}x_1)\operatorname{xor}\cdots\operatorname{xor}(a_{1n}\operatorname{and}x_n)=b_1\\\vdots\\(a_{m1}\operatorname{and}x_1)\operatorname{xor}\cdots\operatorname{xor}(a_{mn}\operatorname{and}x_n)=b_m\end{cases}\Rightarrow[A,b]=A'_{m×(n+1)}\\a_{ij},x_i,b_i\in\{0,1\}\)是01线性异或方程组。
异或运算\(\operatorname{xor}\)\(\Leftrightarrow\)不进位加法。
与运算\(\operatorname{and}\)\(\Leftrightarrow\)模2意义下乘法。
高斯消元化\(A'_{m×(n+1)}\)的A为行最简形,得到\(A''_{m×(n+1)}\):
h和l都从1开始。借助循环依次处理第1~n列。对于第l列:
- 在第h~m行中,找到第l列系数为1的一行h1。
如果存在该系数为1,记录\(key_h=l\)。
否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列。 - 将第h1行与第h行交换。(交换某两行)
- 将上下除第h行外的所有行的第l列清成0。(把某行异或到另一行上去)
- 执行循环底部的h++。
执行后如果h>m,跳出循环;否则执行l++处理完第l列。
r(A)=h-1。
判断解的个数:
- 有解 \(\Leftrightarrow\) r(A)==r([A,b]) \(\Leftrightarrow\) \(\forall i\in[r(A)+1,m]\cap\mathbb{Z},A''_{i,n+1}==0\)。
-
线性异或方程组
- 有唯一解 \(\Leftrightarrow\) r(A) == r([A,b]) == n。
- 否则有无穷多解。
-
01线性异或方程组
解的个数为\(2^{n-r(A)}\)。
-
- 否则无解。
求出一个合法解:
令\(x_{key_i}=A''_{i,n+1},i\in[1,r(A)]\cap\mathbb{Z}\),其余的即为自由变量\(x_j=0\)。
若要求出其他的合法解,则令自由变量\(x_j\)取任意常数,代入方程求出\(x_{key_i}\)。
//i:m;j:n
int m,n;
int a[M][N],x[N];
int ra,key[M];
int gauss()
{
//高斯消元化A'_{m×(n+1)}的A为行最简形,得到A''_{m×(n+1)}
int h,l;
for(h=1,l=1;l<=n;l++) //h和l都从1开始。借助循环依次处理第1~n列
{
//在第h~m行中,找到第l列系数为1的一行h1
int h1=h;
for(int i=h+1;i<=m;i++)
if(a[i][l]==1)
{
h1=i;
break;
}
if(a[h1][l]==1) key[h]=l; //如果存在该系数为1,记录key_h=l
else continue; //否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列
//将第h1行与第h行交换。(交换某两行)
for(int j=l;j<=n+1;j++) swap(a[h][j],a[h1][j]);
//将上下除第h行外的所有行的第l列清成0。(把某行异或到另一行上去)
for(int i=1;i<=m;i++)
{
if(i==h || !a[i][l]) continue;
for(int j=n+1;j>=l;j--) //这里可倒序可正序
a[i][j]^=a[h][j]; //把某行异或到另一行上去,达到下面所有行的第l列清成0的目的
//思维:此时可以把外层循环i看作定值,且a[h][l]=1
}
//执行循环底部的h++
h++;
if(h>m) break; //执行后如果h>m,跳出循环;否则执行l++处理完第l列
}
ra=h-1;
//判断解的个数
for(int i=ra+1;i<=m;i++)
if(a[i][n+1]!=0)
return 0;
return 1<<(n-ra);
}
int cnt=gauss();
printf("%d\n",cnt);
if(cnt)
{
//求出一个合法解
for(int j=1;j<=n;j++) x[j]=0;
for(int i=1;i<=ra;i++) x[key[i]]=a[i][n+1];
for(int j=1;j<=n;j++) printf("%d\n",x[j]);
}
bitset优化01线性异或方程组\(O(\frac{mn^2}{w})\)
//i:m;j:n
int m,n;
bitset<N> a[M];
bool x[N];
int ra,key[M];
int gauss()
{
//高斯消元化A'_{m×(n+1)}的A为行最简形,得到A''_{m×(n+1)}
int h,l;
for(h=1,l=1;l<=n;l++) //h和l都从1开始。借助循环依次处理第1~n列
{
//在第h~m行中,找到第l列系数为1的一行h1
int h1=h;
for(int i=h+1;i<=m;i++)
if(a[i][l]==1)
{
h1=i;
break;
}
if(a[h1][l]==1) key[h]=l; //如果存在该系数为1,记录key_h=l
else continue; //否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列
//将第h1行与第h行交换。(交换某两行)
swap(a[h],a[h1]);
//将上下除第h行外的所有行的第l列清成0。(把某行异或到另一行上去)
for(int i=1;i<=m;i++)
{
if(i==h || !a[i][l]) continue;
a[i]^=a[h]; //把某行异或到另一行上去,达到下面所有行的第l列清成0的目的
}
//执行循环底部的h++
h++;
if(h>m) break; //执行后如果h>m,跳出循环;否则执行l++处理完第l列
}
ra=h-1;
//判断解的个数
for(int i=ra+1;i<=m;i++)
if(a[i][n+1]!=0)
return 0;
return 1<<(n-ra);
}
int cnt=gauss();
printf("%d\n",cnt);
if(cnt)
{
//求出一个合法解
for(int j=1;j<=n;j++) x[j]=0;
for(int i=1;i<=ra;i++) x[key[i]]=a[i][n+1];
for(int j=1;j<=n;j++) printf("%d\n",x[j]);
}
建模应用
-
开关问题
特征:有m个节点和n个操作。每个节点拥有2种状态,每个操作可以切换一些节点的状态,最终的局面的状态与操作的顺序无关。(同一个节点在不同的时间被切换2次后的状态和未被切换的状态相同。)求出从这m个节点的起始局面到这m个节点的目标局面的操作方案。
这个问题符合线性异或方程组。
第i个节点的起始状态和目标状态:\(b_i=st_i\operatorname{xor}ed_i\)。
第i个节点的状态会被第j个操作切换(第j个操作会切换第i个节点的状态):\(a_{ij}=1\);否则,\(a_{ij}=0\)。
解此线性异或方程组:若\(x_j==1\),则需要执行第j个操作;否则,不执行第j个操作。
-
位运算

浙公网安备 33010602011771号