吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 292|回复: 2
收起左侧

[C&C++ 原创] 实现复数及一些基本的运算

[复制链接]
FRSS 发表于 2024-4-2 13:10
在C/C++中,有一个头文件complex,里面实现了复数和一些运算,但是我们不用它,自己写一个复数类。
需求:实现四则运算、输入输出和一些复数版本的数学函数运算
如图为最终效果:
QQ截图20240402114629.png
第一行为-5开平方,第二行为我们输入的复数,然后对其进行运算并输出。
main文件中的代码:
[C++] 纯文本查看 复制代码
#include "Complex.h"
using std::cout, std::cin, std::endl;
int main() {
	cout << "已知负数-5,开平方为:" << csqrt(-5) << endl;
	//Complex z(a, b)为定义一个复数z=a+bi (b可以为负数)
	Complex z;
	cin >> z;
	cout << "输入的复数" << z << endl;
	cout << "复数" << z << "的模为" << z.abs() << endl;
	cout << "复数" << z << "的主辐角为" << z.argz() << endl;
	cout << "复数(" << z << ")+(2+3i) = " << z + Complex(2, 3) << endl;
	cout << "复数(" << z << ")-(2+3i) = " << z - Complex(2, 3) << endl;
	cout << "复数(" << z << ")*(2+3i) = " << z * Complex(2, 3) << endl;
	cout << "复数(" << z << ")/(2+3i) = " << z / Complex(2, 3) << endl;
	cout << "sqrt(" << z << ") = " << csqrt(z) << endl;
	cout << "exp(" << z << ") = " << exp(z) << endl;
	cout << "ln(" << z << ") = " << log(z) << endl;
	cout << "sin(" << z << ") = " << sin(z) << endl;
	cout << "cos(" << z << ") = " << cos(z) << endl;
	cout << "tan(" << z << ") = " << tan(z) << endl;
	return 0;
}

那么,开始吧!

第一步:创建Complex类
复数的标准形式为z=a+bi,所以我们需要两个字段:实部r和虚部img
因此代码为:
[C++] 纯文本查看 复制代码
class Complex {
public:
	//我们需要实现的一些功能
private:
	double r;
	double img;
};

构造函数:我们允许通过调用构造函数Complex(a,b)来创建复数a+bi,因此在public中写一个构造函数,参数为a,b并给出定义:
[C++] 纯文本查看 复制代码
Complex::Complex(double Real, double Imageine)
{
	r = Real;
	img = Imageine;
}

Rel()和Img()函数:两个成员函数,分别返回z的实部和虚部,这里就不做介绍了
模:成员函数abs()为返回复数的模,也很简单,直接return sqrt(r * r + img * img);就行
辐角:一个复数的辐角有无数个,这里只返回主值。根据百度对辐角的定义,完成代码:
[C++] 纯文本查看 复制代码
double Complex::argz()
{
	const double pi = 3.141592654;
	if (this->r == 0 && this->img == 0) throw std::domain_error("Undefined");
	if (this->r > 0) return atan(this->img / this->r);
	else if (this->r == 0 && this->img > 0) return pi / 2;
	else if (this->r == 0 && this->img < 0) return -pi / 2;
	else if (this->r < 0 && this->img >= 0) return atan(this->img / this->r) + pi;
	else if (this->r < 0 && this->img < 0)return atan(this->img / this->r) - pi;
	else return NAN;
}

第二步:重载输入、输出运算符
重载输出运算符:在头文件中定义友元函数operator<<,然后在cpp文件中实现
注意:重载的输出运算符并不是简单的cout <<z.r<<"+"<<z.img<<"i";,而是会根据实部、虚部的符号进行输出,例如Complex(0,4)会输出4i而不是0+4i,Complex(1,-2)会输出1+-2i
分析思路:
如果z.r==0(纯虚数),那就判断img的符号,如果是正就直接cout <<z.img<<"i";,否则就cout <<"-"<<-a.img<<"i";
标准形式同理,根据虚部的符号进行输出,代码:
[C++] 纯文本查看 复制代码
ostream& operator<<(ostream& os,const Complex& a) {
	if (a.img == 0) os << a.r;	//如果不是一个复数,就直接输出实部
	if (a.r == 0) {
		if (a.img > 0) os << a.img << "i";
		else if (a.img < 0) os << "-" << -a.img << "i";
	}
	else {
		if (a.img > 0) os << a.r << "+" << a.img << "i";
		else if (a.img < 0) os << a.r << "-" << -a.img << "i";
	}
	return os;
}

这样,我们就能直接cout << z来输出一个复数
重载输入运算符:这个应该是最复杂的了,我写了好久才勉强写出来一个石山代码,具体思路是用string存储输入,使用正则表达式判断是否为复数的输入格式,如果不是就直接抛出一个异常,是的话就分别将实部和虚部(包括符号)拷贝到一个char*中,然后使用std::stod转换为double类型存储到z.r和z.img中。这样输入a+bi时都会被识别为一个复数,缺点就是输入纯虚数bi时还是要输入0+bi、虚部为1时1不能省略。代码如下:
[C++] 纯文本查看 复制代码
istream& operator>>(istream& is, Complex& a)
{
	std::string input;
	is >> input;
	std::regex complex("^(0|[-+]?\\d+(\\.\\d+)?)([-+]?\\d+(\\.\\d+)?)i?$");
	bool is_complex = std::regex_match(input, complex);
	if (!is_complex) throw std::exception("Invalid complex number!");
 	int s = 1;
	char Real[50]{ 0 };
	char Img[50]{ 0 };
	//考虑到第一位如果是符号
	Real[0] = input[0];
	for (int i = 1; (input[i] < '9' && input[i] > '0') || input[s] == '.'; i++) {
		if (i > 48) break;
		Real[i] = input[i];
		s++;
	}
	Img[0] = input[s++];
	for (int i = 1; (input[s] <= '9' && input[s] >= '0') || input[s]=='.'; i++) {
		if (i > 48) break;
		Img[i] = input[s];
		s++;
	}
	a.r = std::stod(Real);
	a.img = std::stod(Img);
	return is;
}

这样,我们就能够直接cin>>z;来按照a+bi的格式输入一个复数
第三步:重载一些基本的数学函数
首先想到的就是梦开始的地方:根号
由于sqrt函数已经被定义了,因此这里创建一个sqrt的扩展版函数csqrt,它接受一个参数x作为被开方数,x可以是任何实数,代码如下:
[C++] 纯文本查看 复制代码
Complex __cdecl csqrt(double x)
{
	if (x >= 0) return Complex(sqrt(x), 0);
	else return Complex(0, sqrt(-x));
}

这样,如果被开方数非负,返回虚部为0的数,为负数就返回sqrt(-x)i
根据 QQ截图20240402125247.png 能得到s^z即pow函数的定义
但是呢,我们还是需要先给出log(z)的定义,想要log(z)的定义,我们还需要补充log(x)在x<0的定义(开始套娃)
log(x)在x=0处无定义,在x<0时多值,这里只考虑主值
log(-x)(x为正数)都可以化为log(x)+log(-1),所以只需根据欧拉公式的指数形式替换-1=i^2得到log(i)的值然后再次替换即可得到log(-1)的值,代码如下:
[C++] 纯文本查看 复制代码
Complex __cdecl clog(double x)
{
	//ln(-1) = pi*i
	Complex logngo(0, 3.141592654);
	if (x > 0) return Complex(log(x), 0);
	else if (x < 0) return logngo + Complex(log(-x), 0);
	else throw std::domain_error("Undefined");
}

注意:在此之前要先重载+-*/运算符,因为比较简单,就不提了
然后根据log(z)的定义 QQ截图20240402130519.png 即可完成代码,这里使用的定义可能不一样,但返回的结果是一样的。代码如下:
[C++] 纯文本查看 复制代码
Complex __cdecl log(const Complex& x) 
{
	if (x.img == 0) return clog(x.r);//Complex(log(x.r), 0);
	const double pi = 3.141592654;
	if (x.r == 0) return clog(x.img) + Complex(0, pi / 2);
	double xita = atan(x.img / x.r);
	return clog(x.r / cos(xita)) + Complex(0, xita);
}

完美,这下可以写pow函数,也可以重载sqrt函数使它支持复数作为被开方数

如果你看懂了,你可以尝试:
根据复三角函数的定义重载sin、cos、tan、exp函数
完成pow函数的代码编写

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

woaipoie 发表于 2024-4-8 14:44
几乎忘完了
yanger3999 发表于 2024-4-8 15:42
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-7 08:15

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表