线性代数代码实现(六)矩阵除法(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)


相关推荐

  • 《这是全网最硬核redis总结,谁赞成,谁反对?》六万字大合集

    后端需要知道的关于redis的事,基本都在这里了。此文后续会改为粉丝可见,所以喜欢的请提前关注。你的点赞和评论是我创作的最大动力,谢谢。《三天给你聊清楚redis》第1天先唠唠redis是个啥(18629字)一、入门Redis是一款基于键值对的NoSQL数据库,它的值支持多种数据结构:字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sortedsets)等。•Redis将所有的数据都存放在内存中,所以它的读写性能十分惊人,.

  • Nginx搭建小型图片服务器

    Nginx搭建小型图片服务器

  • vs 证书签名

    vs 证书签名在VS2005或VS2008安装后发现路径**VC7\Common7\Tools\Bin\下有许多小工具,我对带有Cert的文件比较感兴趣。经过研究,发现是有关证书和签名的,当然,该证书未被证书认证机构认可。下面列出这些工具和我的使用心得。1。Makecert.exe—证书创建工具2。Cert2Spc.exe—发行者证书测试工具3。Signcode.exe—文件签名工

  • 第 3.4 节 MySQL

    第 3.4 节 MySQL

  • 华为吴晟:分布式监控系统的设计与实现

    华为吴晟:分布式监控系统的设计与实现

  • Anaconda环境下Tensorflow的安装与卸载

    Anaconda环境下Tensorflow的安装与卸载Anaconda环境下Tensorflow的安装与卸载文章目录Anaconda环境下Tensorflow的安装与卸载一、环境的创建与删除1.查看自己配置的环境2.配置一个新的环境3.进入和退出环境4.删除环境二、包(第三方库)的安装与卸载1.查看安装的包2.安装包3.删除包4.更新包三、tensorflow的安装与卸载1.创建一个tensorflow环境2.激活tensorflow环境3.安装tensorflow4.查看是否安装成功5.查看tensorflow的版本号6.退出tensorflow环境一

发表回复

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

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