前置知识
需要以下前置知识:
1.矩阵的逆及其存在的判定方法
2.初等矩阵及其性质
矩阵的逆
由于我们在定义矩阵运算的时候只定义了数乘和矩阵乘法,而没有除法运算。和逆元的产生一样,我们为了定义出除法,我们采用乘一个数/矩阵得到单位1/单位矩阵的方法,并定义这个数/矩阵为原数/原矩阵的乘法。
注:单位矩阵是一个除了主对角线为1,其他全为0的方阵。由于阶数不固定,因而有无穷多种单位矩阵。
下文有更简单的判定和计算方法。
初等矩阵
把单位矩阵进行一次初等行变换,就得到了初等矩阵。其中,初等行变换有以下三种:
1.交换矩阵的任意两行。
2.用一个非零整数 kk乘矩阵的任意一行。
3.将矩阵中某一行乘以 kk倍加到另外一行。
因此上图均为初等矩阵。
由于初等行变换可逆(可以改过去又可以改回来),因此初等矩阵可逆。
证明:设 E为一初等矩阵,由于 EI=E,因此任意一个初等矩阵可以视为对 I矩阵的一种变换,使其变为 E矩阵。由于初等行变换可逆,则存在 E变换的逆变换 F,将 E矩阵变回 I,因此 EF=I,即 E可逆,且其逆为 E的逆变换。
到此可以引出本题的证明了:
接下来就是解法时间
代码部分
涉及逆元和费马小定理,可自查
//求逆矩阵
#include <cstdio>
#include <algorithm>
using namespace std;
const long long mod = 1000000007;
long long power(long long a,int x)//快速幂板子
{
long long ans = 1;
while(x)
{
if(x&1)
{
ans *= a;
ans %= mod;
}
a *= a;
a %= mod;
x >>= 1; //<<=等价于乘以2 >>=等价与除以2
}
return ans % mod;
}
long long a[405][805];
int main()
{
int n, m;
scanf("%d", &n);
m = 2 * n;//矩阵的宽
for (int i = 1; i <= n;i++)
{
for (int j = 1; j <= n;j++)
scanf("%lld", &a[i][j]);
a[i][i + n] = 1;//后面要跟上一个n阶单位矩阵
}
for (int i = 1; i <= n; i++)//高斯-若尔当消元的板子
{
int place = i;
for (int j = i + 1; j <= n; j++)//找到绝对值最大的元素开始消元
if(abs(a[j][i])>abs(a[place][i]))
place = j;
if (i != place)
swap(a[i], a[place]);
if(!a[i][i])//如果某行没有主元则A无法化为单位矩阵,无解
{
printf("No Solution");
return 0;
}
long long inv = power(a[i][i], mod - 2);//本题加入的逆元特色
for (int j = 1; j <= n; j++)
if(j!=i)
{
long long multiple = a[j][i] * inv % mod;//等价于除以a[i][i],消去其他行在第i列上的数,使之变成简化阶梯形矩阵
for (int k = i; k <= m; k++)
a[j][k] = ((a[j][k] - a[i][k] * multiple) % mod + mod) % mod;
}
for (int j = 1; j <= m; j++)//由于此处需要简化阶梯型矩阵,要把原矩阵化为简化矩阵的必须操作。
//“在使用高斯-若尔当消元的时候,计算机计算的时候通常采用回带法,而人操作的时候建议采用此法。”——《线性代数及其应用》
a[i][j] = (a[i][j] * inv % mod);
}
for (int i = 1; i <= n;i++)
{
for (int j = n + 1; j <= m; j++)//只打印后面,前面的单位矩阵不要打出来了
printf("%lld ", a[i][j]);
printf("\n");
}
return 0;
}