线性代数代码实现(六)矩阵除法(C++)

线性代数代码实现(六)矩阵除法(C++)前言:距离上一篇文章发布已经五天过去了,在这里先给一直等待的伙伴们说声抱歉,因为博主最近的事情很多,只好暂时停更,望大家理解!上一篇文章中,我们介绍了求解逆矩阵的方法,我提到,可逆矩阵可以定义除法。这一篇文章中,讨论一下怎样实现矩阵除法!一、线性代数知识回顾:事实上,矩阵没有“除法”这一概念,我们的“除法”实际上是用以下方式来定义的:设矩阵,,,其中为可逆矩阵,满足以下等式:变换得:如果我们换一种写法,就成了:这样就定义了矩阵除法,我把它称…

大家好,又见面了,我是你们的朋友全栈君。

前言:

        距离上一篇文章发布已经五天过去了,在这里先给一直等待的伙伴们说声抱歉,因为博主最近的事情很多,只好暂时停更,望大家理解!上一篇文章中,我们介绍了求解逆矩阵的方法,我提到,可逆矩阵可以定义除法。这一篇文章中,讨论一下怎样实现矩阵除法!

一、线性代数知识回顾:

事实上,矩阵没有 “除法” 这一概念,我们的 “除法” 实际上是用以下方式来定义的:

设矩阵ABC,其中B为可逆矩阵,满足以下等式:

BA=C

变换得:

A=B^{-1}C

如果我们换一种写法,就成了:

A=\frac{C}{B} 

这样就定义了矩阵除法,我把它称为 “左除”。类似的,若:

A=CB^{-1}

我们这时如果定义出 A=\frac{C}{B} ,那么就成为了 “右除”。 

本篇以 “左除” 为例,给出算法与代码。

之前我们给出过矩阵的乘法求矩阵的逆的方法,因此我们可以很简单地写出求矩阵除法的代码,只需调用两个函数即可,但是,我们还是要挑战一下!优化一下算法

之前提到过线性代数一个定理:

对一个矩阵左乘一个可逆矩阵相当于对这个矩阵作一系列初等行变换

求解 \frac{C}{B} 就相当于根据 等式 BA=C 来求解矩阵 A ,我们看下面的推导:

由 BA=C 可知: A=B^{-1}BA=EA=B^{-1}C

这就说明,对等式 BA=C 两边都左乘 B^{-1},相当于作一系列初等行变换,观察等式,这个初等行变换将等式左边的矩阵 B 化为矩阵 E ,将等式右边的矩阵 C 变为 B^{-1}C,也就是矩阵 A 。

所以对矩阵 B 做初等行变换化为 E 的同时,对矩阵 C 做同样的初等行变换,就将矩阵 C 变成了我们要求解的矩阵 A

二、代码实现:

 由于本次博客算法与求逆矩阵类似,所以博主就不在这里设置算法设计的环节了,直接进入代码,有的细节在代码里面有注释。

类的定义:

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账号...

(0)
blank

相关推荐

  • Git可视化教程——Git Gui的使用[通俗易懂]

    Git可视化教程——GitGui的使用

  • MacBook 屏幕录制 soundflower 只录内屏声音 无外界声音

    MacBook 屏幕录制 soundflower 只录内屏声音 无外界声音MacBook屏幕录制只包含内屏声音无外界录音目的录屏方法办法目的用Mac自带的QuickTimePlayer录制屏幕的时候(或者按快捷键⇧+⌘+5),三个选项:1)无声音2)选外置扬声器。电脑外放,确实能录到内屏声音,但是扬声器收录的人声、环境音也会录进来3)插耳机后,可以选择耳机。这样内屏声音也没了,只有耳机口的收音被录进来录屏方法办法下载插件soundflower:soundflower下载地址一开始可能下载失败,提示“来自开发者MATTINGALLS的系统软件已被阻止载

  • python中os.getcwd的作用_python中getitem是什么意思

    python中os.getcwd的作用_python中getitem是什么意思比如我们想要获取当前目录的路径,有人可能会用到getcwd()方法,但是会发现这个方法有时候不好用。下面介绍一下,getcwd()方法的具体细节:1、os.getcwd():获取当前工作目录,也就是

  • Python获取Websocket接口的数据

    Python获取Websocket接口的数据作者:小小明在前面的用Tornado实现web聊天室一文中介绍了python实现websocket的方法,这篇文章将要分享如何用python作为客户端获取websocket接口的数据。前文链接:https://blog.csdn.net/as604049322/article/details/112386560websocket的使用WebSocket是一种在单个TCP/TSL连接上,进行全双工、双向通信的协议。WebSocket可以让客户端与服务器之间的数据交换变得更加简单高效,服务端.

  • mybatis一级缓存和二级缓存失效_mybatis二级缓存默认开启吗

    mybatis一级缓存和二级缓存失效_mybatis二级缓存默认开启吗1.缓存介绍Mybatis提供查询缓存,如果缓存中有数据就不用从数据库中获取,用于减轻数据压力,提高系统性能。Mybatis的查询缓存总共有两级,我们称之为一级缓存和二级缓存,如图:一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相…

  • linux启动ftp命令_linux安装ftp命令

    linux启动ftp命令_linux安装ftp命令ftp服务器在网上较为常见,Linuxftp命令的功能是用命令的方式来控制在本地机和远程机之间传送文件下面由学习啦小编为大家整理了linux下开启ftp命令的相关知识,希望对大家有所帮助!linux下启动FTP命令的方式一般linux都有vsftpd吧,启动命令是servicevsftpdstart,你要限制匿名登录的话,修改它的配置文件/etc/vsftpd/vsftpd.conf,把an…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号