矩阵求逆


矩阵求逆

矩阵乘法运算

复习一下矩阵乘法的运算规则。

矩阵快速幂

行列式

有:

则:

逆矩阵

现在有矩阵:

什么是逆矩阵呢?

就是我们要找到一个 $A^{-1}$,使得:

二阶矩阵求逆直接套用公式即可:

所以

的逆矩阵为:

初等变换

基本求逆矩阵的方法。

假设我们现在有一个三阶矩阵:

我们需要一个伴随矩阵 $E$,当 $A$ 变成 $E$ 时,$E$ 即为 $A^{-1}$。

虚线右边即为伴随矩阵。

这是我们通过不断的消数,使 $A$ 变成 $E$ 的样子。

消数的方式是将两整行相减。或者是将整行乘或除一个数,使关键位置变成 $1$。

以下为其中一种过程:

消去第二行第一列的 $6$,可以让第二行减去六倍的第一行。

将第二行关键位置变成 $1$,整行 $\times -\frac{1}{16}$。

消去第三行第二列的 $1$,让第三行将去第二行即可。

将第三行关键位置变成 $1$,整行 $\times \frac{4}{3}$。

将第一行第二列的 $3$ 消掉,第一行减去三倍的第二行……

笔者的方法可能比较麻烦(一边先通过行之间加减,只剩关键位置,再处理整行的乘除),但是通过类似的方式,就会得到:

此时的

注意

有时一行的关键位置为零,可以通过交换两行,或加减其它行,来解决问题。

例如:

交换一二行。

交换二三行。

现在就可以对矩阵进行处理,得带逆矩阵了。

逆矩阵无解

如果一行消项时,全部为零($A$ 矩阵,不包括伴随矩阵),那么矩阵无逆。

程序思路

P4783 【模板】矩阵求逆

  • 找到有效值(不为 $0$ 的值,可以当成关键位置使用)

  • 将有效值所在行、列与当前行、当前列交换,并且记录下交换的行和列

  • 把当前行元素乘关键位置的逆元(同时判无解:逆元不存在)

  • 若果要将关键位置化为 $1$,整行乘关键位置的逆元即可

  • 对矩阵中的其他行做和 高斯消元 几乎完全一样的操作,只有每行的有关键位置列不一样。

  • 最后把第 $2$ 步中的交换逆序地交换回来。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<cctype>
using namespace std;
const long long m_d=1e9+7;
const int o_o=1e3+10; 
long long a_a[o_o][o_o];
int n,i_x[o_o],j_x[o_o];
long long r_r(){//快读 
    long long x=0,f=1;
    char c=getchar();
    while(!isdigit(c)){
        if(c=='-')f=-1;
        c=getchar();
    }
    while(isdigit(c)){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
void e_g(int a,int b,int &x,int &y){//找逆元 
    if(!b){
        x=1;
        y=0;
        return;
    }
    e_g(b,a%b,y,x);
    y-=x*(a/b);
}
int n_y(int p){//逆元 
    int x,y;
    e_g(p,m_d,x,y);
    return (x+m_d)%m_d;
}
void c_z(){
    for(int k=1;k<=n;k++){//每行进行遍历 
        for(int i=k;i<=n;i++)
            for(int j=k;j<=n;j++)
            if(a_a[i][j]){
                i_x[k]=i,j_x[k]=j;//找到一个有效值(不为 0 的值) 
                break;
            }
        for(int i=1;i<=n;i++)//交换行 
            swap(a_a[k][i],a_a[i_x[k]][i]);
        for(int i=1;i<=n;i++)//交换列 
            swap(a_a[i][k],a_a[i][j_x[k]]);
        if(!a_a[k][k]){//找不到有效值,无解 
            puts("No Solution");
            exit(0);//结束程序 
        }
        a_a[k][k]=n_y(a_a[k][k]);//关键位置求逆元 
        for(int j=1;j<=n;j++)//关键位置变为 1,整行都要乘它的逆元 
            if(j!=k)(a_a[k][j]*=a_a[k][k])%=m_d;
        for(int i=1;i<=n;i++)
            if(i!=k)
                for(int j=1;j<=n;j++)//每个位置的数更新 
                    if(j!=k)(a_a[i][j]+=m_d-a_a[i][k]*a_a[k][j]%m_d)%=m_d;
        for(int i=1;i<=n;i++)//消列 
            if(i!=k)a_a[i][k]=(m_d-a_a[i][k]*a_a[k][k]%m_d)%m_d;
    }
    for(int k=n;k;k--){//交换的行列交换回来 
        for(int i=1;i<=n;i++)swap(a_a[j_x[k]][i],a_a[k][i]);
        for(int i=1;i<=n;i++)swap(a_a[i][i_x[k]],a_a[i][k]);
        
        //注意,j_x,i_x 的位置,改变,要正确的变换,以下错误 
        /*
            for(int i=1;i<=n;i++)swap(a_a[i_x[k]][i],a_a[k][i]);
            for(int i=1;i<=n;i++)swap(a_a[i][j_x[k]],a_a[i][k]);
        */
    }
}
int main(){
    n=r_r();
    for(int i=1;i<=n;i++)//读入矩阵 
        for(int j=1;j<=n;j++)a_a[i][j]=r_r();
    c_z();//对矩阵操作 
    for(int i=1;i<=n;i++){//输出逆矩阵 
        for(int j=1;j<=n;j++)printf("%lld ",a_a[i][j]);
        puts("");
    }
    return 0;
}

文章作者: 王大神——A001
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 王大神——A001 !
  目录