【数学·线性代数】线性方程组

2.2.线性方程组

2.2.1.初等矩阵变换

  1. 把某一行乘一个非零的数;
  2. 交换某两行;
  3. 把某行的若干倍加到另一行上去。

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列:

  1. 在第h~m行中,找到第l列系数的绝对值最大的一行\(h_{max}\)

    如果该系数不为0,记录\(key_h=l\)

    否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列。

  2. 将第\(h_{max}\)行与第h行交换。(交换某两行)

  3. 将交换后的第h行的第一个有效数字(第l列)变成1。(把某一行乘一个非零的数)

  4. 将上下除第h行外的所有行的第l列清成0。(把某行的若干倍加到另一行上去)

  5. 执行循环底部的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列:

  1. 在第h~m行中,找到第l列系数为1的一行h1。
    如果存在该系数为1,记录\(key_h=l\)
    否则,第h~m行的第l列全是0,结束本轮循环,不要执行循环底部的h++,只执行l++处理完第l列。
  2. 将第h1行与第h行交换。(交换某两行)
  3. 将上下除第h行外的所有行的第l列清成0。(把某行异或到另一行上去)
  4. 执行循环底部的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]);
}

建模应用

  1. 开关问题

    特征:有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个操作。

  2. 位运算

posted @ 2025-06-26 23:10  Brilliance_Z  阅读(43)  评论(0)    收藏  举报