大家好,又见面了,我是你们的朋友全栈君。
前言:
距离上一篇文章发布已经五天过去了,在这里先给一直等待的伙伴们说声抱歉,因为博主最近的事情很多,只好暂时停更,望大家理解!上一篇文章中,我们介绍了求解逆矩阵的方法,我提到,可逆矩阵可以定义除法。这一篇文章中,讨论一下怎样实现矩阵除法!
一、线性代数知识回顾:
事实上,矩阵没有 “除法” 这一概念,我们的 “除法” 实际上是用以下方式来定义的:
设矩阵,,,其中为可逆矩阵,满足以下等式:
变换得:
如果我们换一种写法,就成了:
这样就定义了矩阵除法,我把它称为 “左除”。类似的,若:
我们这时如果定义出 ,那么就成为了 “右除”。
本篇以 “左除” 为例,给出算法与代码。
之前我们给出过矩阵的乘法和求矩阵的逆的方法,因此我们可以很简单地写出求矩阵除法的代码,只需调用两个函数即可,但是,我们还是要挑战一下!优化一下算法:
之前提到过线性代数一个定理:
对一个矩阵左乘一个可逆矩阵相当于对这个矩阵作一系列初等行变换
求解 就相当于根据 等式 来求解矩阵 ,我们看下面的推导:
由 可知:
这就说明,对等式 两边都左乘 ,相当于作一系列初等行变换,观察等式,这个初等行变换将等式左边的矩阵 化为矩阵 ,将等式右边的矩阵 变为 ,也就是矩阵 。
所以对矩阵 做初等行变换化为 的同时,对矩阵 做同样的初等行变换,就将矩阵 变成了我们要求解的矩阵
二、代码实现:
由于本次博客算法与求逆矩阵类似,所以博主就不在这里设置算法设计的环节了,直接进入代码,有的细节在代码里面有注释。
类的定义:
class Mat
{
public:
int m = 1, n = 1; //行数和列数
double mat[N][N] = { 0 }; //矩阵开始的元素
Mat() {}
Mat(int mm, int nn)
{
m = mm; n = nn;
}
void create();//创建矩阵
void Print();//打印矩阵
bool div(Mat a,Mat b);//求 a.mat/b.mat
};
其中 div 函数做除法运算,在求逆矩阵代码的基础上修改一下就可以了,代码如下:
bool Mat::div(Mat a,Mat b)
{
if (b.n != b.m)
{
cout << "奇异矩阵不能作分母!" << endl;
return false;
}
if (b.n != a.m)
{
cout << "这两个矩阵无法相除!" << endl;
return false;
}
//下来进行自上而下的初等行变换,使得矩阵 b.mat 变成单位上三角矩阵
for (int i = 1; i <= b.m; i++) //注意这里要 i<=m,和之前的上三角矩阵有不同
{ //因为要判断最后一行化为上三角矩阵的最后一行最后一列元素是否为 0
//寻找第 i 列不为零的元素
int k;
for (k = i; k <= b.m; k++)
{
if (fabs(b.mat[k][i]) > 1e-10) //满足这个条件时,认为这个元素不为0
break;
}
if (k <= b.m)//说明第 i 列有不为0的元素
{
if (k != i)//说明第 i 行 第 i 列元素为零,需要和其他行交换
{
//交换第 i 行和第 k 行所有元素
for (int j = i; j <= b.n; j++)//从第 i 个元素交换即可,因为前面元素都为零
{//使用mat[0][j]作为中间变量交换元素
b.mat[0][j] = b.mat[i][j]; b.mat[i][j] = b.mat[k][j]; b.mat[k][j] = b.mat[0][j];
}
for (int j = 1; j <= a.n; j++)//从第 1 个元素交换
{
a.mat[0][j] = a.mat[i][j]; a.mat[i][j] = a.mat[k][j]; a.mat[k][j] = a.mat[0][j];
}
}
double c = b.mat[i][i];//倍数
//将矩阵 a.mat 的主对角线元素化为 1
for (int j = i; j <= b.n; j++)//从第 i 个元素开始,前面元素都为 0
{
b.mat[i][j] /= c;
}
for (int j = 1; j <= a.n; j++)//给分子矩阵作同样的变换
{//从第 1 个元素开始
a.mat[i][j] /= c;
}
for (int j = i + 1; j <= b.m; j++)
{
//注意本来为 -b.mat[j][i]/b.mat[i][i],因为b.mat[i][i]等于 1,则不需要除它
c = -b.mat[j][i];
for (k = i; k <= b.n; k++)//从第 i 个元素开始
{
b.mat[j][k] += c * b.mat[i][k];//第 i 行 b 倍加到第 j 行
}
for (k = 1; k <= b.n; k++)//从第 1 个元素开始
{
a.mat[j][k] += c * a.mat[i][k];
}
}
}
else
{
cout << "奇异矩阵不能作分母!" << endl;
return false;
}
}
//下面进行自下而上的行变换,将 b.mat 矩阵化为单位矩阵
for (int i = b.m; i > 1; i--)
{
for (int j = i - 1; j >= 1; j--)
{
double c = -b.mat[j][i];
b.mat[j][i] = 0; //实际上是通过初等行变换将这个元素化为 0,
for (int k = 1; k <= a.n; k++)
{//通过相同的初等行变换来变换右边矩阵
a.mat[j][k] += c * a.mat[i][k];
}
}
}
//下面代码将经过初等行变换的分子赋值给类中的矩阵
m = a.m; n = a.n;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
mat[i][j] = a.mat[i][j];
}
}
return true;
}
我们在这篇博客中只给出了 “左除” ,有兴趣的朋友们可以试着自己做一下 “右除” ,基本方法是一样的,自己写一下可以更好地掌握并且锻炼自己的代码能力。这里给出完整代码:
#include<iostream>
#include <cmath>
#define N 10
using namespace std;
class Mat
{
public:
int m = 1, n = 1; //行数和列数
double mat[N][N] = { 0 }; //矩阵开始的元素
Mat() {}
Mat(int mm, int nn)
{
m = mm; n = nn;
}
void create();//创建矩阵
void Print();//打印矩阵
bool div(Mat a,Mat b);//求 a.mat/b.mat
};
void Mat::create()
{
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> mat[i][j];
}
}
}
void Mat::Print()
{
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
cout << mat[i][j] << "\t";
}
cout << endl;
}
}
bool Mat::div(Mat a,Mat b)
{
if (b.n != b.m)
{
cout << "奇异矩阵不能作分母!" << endl;
return false;
}
if (b.n != a.m)
{
cout << "这两个矩阵无法相除!" << endl;
return false;
}
//下来进行自上而下的初等行变换,使得矩阵 b.mat 变成单位上三角矩阵
for (int i = 1; i <= b.m; i++) //注意这里要 i<=m,和之前的上三角矩阵有不同
{ //因为要判断最后一行化为上三角矩阵的最后一行最后一列元素是否为 0
//寻找第 i 列不为零的元素
int k;
for (k = i; k <= b.m; k++)
{
if (fabs(b.mat[k][i]) > 1e-10) //满足这个条件时,认为这个元素不为0
break;
}
if (k <= b.m)//说明第 i 列有不为0的元素
{
if (k != i)//说明第 i 行 第 i 列元素为零,需要和其他行交换
{
//交换第 i 行和第 k 行所有元素
for (int j = i; j <= b.n; j++)//从第 i 个元素交换即可,因为前面元素都为零
{//使用mat[0][j]作为中间变量交换元素
b.mat[0][j] = b.mat[i][j]; b.mat[i][j] = b.mat[k][j]; b.mat[k][j] = b.mat[0][j];
}
for (int j = 1; j <= a.n; j++)//从第 1 个元素交换
{
a.mat[0][j] = a.mat[i][j]; a.mat[i][j] = a.mat[k][j]; a.mat[k][j] = a.mat[0][j];
}
}
double c = b.mat[i][i];//倍数
//将矩阵 a.mat 的主对角线元素化为 1
for (int j = i; j <= b.n; j++)//从第 i 个元素开始,前面元素都为 0
{
b.mat[i][j] /= c;
}
for (int j = 1; j <= a.n; j++)//给分子矩阵作同样的变换
{//从第 1 个元素开始
a.mat[i][j] /= c;
}
for (int j = i + 1; j <= b.m; j++)
{
//注意本来为 -b.mat[j][i]/b.mat[i][i],因为b.mat[i][i]等于 1,则不需要除它
c = -b.mat[j][i];
for (k = i; k <= b.n; k++)//从第 i 个元素开始
{
b.mat[j][k] += c * b.mat[i][k];//第 i 行 b 倍加到第 j 行
}
for (k = 1; k <= b.n; k++)//从第 1 个元素开始
{
a.mat[j][k] += c * a.mat[i][k];
}
}
}
else
{
cout << "奇异矩阵不能作分母!" << endl;
return false;
}
}
//下面进行自下而上的行变换,将 b.mat 矩阵化为单位矩阵
for (int i = b.m; i > 1; i--)
{
for (int j = i - 1; j >= 1; j--)
{
double c = -b.mat[j][i];
b.mat[j][i] = 0; //实际上是通过初等行变换将这个元素化为 0,
for (int k = 1; k <= a.n; k++)
{//通过相同的初等行变换来变换右边矩阵
a.mat[j][k] += c * a.mat[i][k];
}
}
}
//下面代码将经过初等行变换的分子赋值给类中的矩阵
m = a.m; n = a.n;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
mat[i][j] = a.mat[i][j];
}
}
return true;
}
int main()
{
Mat a(3, 4), b(3, 3);
cout << "请输入 " << a.m << "*" << a.n << " 的矩阵:" << endl;
a.create();
cout << "请输入 " << b.m << "*" << b.n << " 的矩阵:" << endl;
b.create();
Mat c;
if (c.div(a, b))
c.Print();
return 0;
}
由于博主最近事情特别多,在接下来的一段时间里很可能会不更新,望大家见谅!
若有不足之处,欢迎大家指正!
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/150201.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...