内容

名称

Math::Complex - 复数及相关数学函数

概要

use Math::Complex;

$z = Math::Complex->make(5, 6);
$t = 4 - 3*i + $z;
$j = cplxe(1, 2*pi/3);

描述

此程序包允许您创建和处理复数。默认情况下,Perl 仅限于实数,但额外的 use 语句带来了完全的复数支持,以及一组通常与复数相关和/或扩展到复数的完整数学函数。

如果您想知道复数是什么,它们被发明出来是为了能够解决以下方程式

x*x = -1

根据定义,解记为i(工程师使用j,因为i通常表示强度,但名称无关紧要)。数字i是一个纯数。

纯虚数的算术就像您对实数所期望的那样...您只需要记住

i*i = -1

因此您有

5i + 7i = i * (5 + 7) = 12i
4i - 3i = i * (4 - 3) = i
4i * 2i = -8
6i / 2i = 3
1 / i = -i

复数是既有实部又有虚部的数,通常记为

a + bi

其中a实部b虚部。复数的算术很简单。你必须跟踪实部和虚部,但除此之外,用于实数的规则也适用

(4 + 3i) + (5 - 2i) = (4 + 5) + i(3 - 2) = 9 + i
(2 + i) * (4 - i) = 2*4 + 4i -2i -i*i = 8 + 2i + 1 = 9 + 2i

复数的图形表示可以在一个平面上(也称为复平面,但它实际上是一个 2D 平面)。数字

z = a + bi

是坐标为 (a, b) 的点。实际上,它将是从 (0, 0) 到 (a, b) 的向量。由此得出,两个复数的加法是一个向量加法。

由于 2D 平面中的点和复数之间存在双射(即映射是唯一的和可逆的),因此复数也可以用极坐标唯一地标识

[rho, theta]

其中rho是到原点的距离,theta是向量和x轴之间的角度。使用指数形式对此有一个表示法,即

rho * exp(i * theta)

其中i是上面介绍的著名的虚数。此形式和笛卡尔形式a + bi之间的转换是立即的

a = rho * cos(theta)
b = rho * sin(theta)

也可以用此公式表示

z = rho * exp(i * theta) = rho * (cos theta + i * sin theta)

换句话说,它是向量在xy轴上的投影。数学家称rho范数,称theta为复数的幅角z范数在此标记为abs(z)

极坐标表示法(也称为三角表示法)对于执行复数的乘法和除法非常方便,而笛卡尔表示法更适合加法和减法。实数在x轴上,因此ytheta为零或pi

可以在实数上执行的所有常见操作都被定义为也可以对复数执行,并且仅仅是对实数上定义的操作的扩展。这意味着当没有虚部时,它们会保持其自然含义,前提是该数字在其定义集中。

例如,计算其参数的平方根的 sqrt 例程仅针对非负实数定义,并产生非负实数(它是从 R+R+ 的应用)。如果我们允许它返回一个复数,那么它可以扩展到负实数,成为从 RC(复数集)的应用

sqrt(x) = x >= 0 ? sqrt(x) : sqrt(-x)*i

它还可以扩展为从 CC 的应用,而它对 R 的限制通过使用以下定义表现为如上所定义

sqrt(z = [r,t]) = sqrt(r) * exp(i * t/2)

事实上,一个负实数可以记为 [x,pi](模数 x 始终是非负的,所以 [x,pi] 实际上是 -x,一个负数),而上述定义说明

sqrt([x,pi]) = sqrt(x) * exp(i*pi/2) = [sqrt(x),pi/2] = sqrt(x)*i

这正是我们上面为负实数定义的内容。sqrt 只返回一个解:如果你想要两个解,请使用 root 函数。

所有定义在实数上的常见数学函数都扩展到了复数,它们共享在虚部为零时以照常方式工作的相同属性(否则,它不会被称为扩展,是吗?)。

在复数上可能的一个操作是实数的恒等式,称为共轭,并在此处用数字上方的水平线或 ~z 表示。

 z = a + bi
~z = a - bi

简单... 现在看

z * ~z = (a + bi) * (a - bi) = a*a + b*b

我们看到 z 的范数记为 abs(z),并定义为到原点的距离,也称为

rho = abs(z) = sqrt(a*a + b*b)

所以

z * ~z = abs(z) ** 2

如果 z 是一个纯实数(即 b == 0),则上述产生

a * a = abs(a) ** 2

这是正确的(abs 对实数有常规含义,即表示绝对值)。此示例解释了为什么 z 的范数记为 abs(z):它将 abs 函数扩展到复数,但当复数实际上没有虚部时,它是我们知道的常规 abs... 这事后证明了我们对范数使用 abs 符号的合理性。

操作

给定以下符号

z1 = a + bi = r1 * exp(i * t1)
z2 = c + di = r2 * exp(i * t2)
z = <any complex or real number>

以下(重载)操作支持复数

z1 + z2 = (a + c) + i(b + d)
z1 - z2 = (a - c) + i(b - d)
z1 * z2 = (r1 * r2) * exp(i * (t1 + t2))
z1 / z2 = (r1 / r2) * exp(i * (t1 - t2))
z1 ** z2 = exp(z2 * log z1)
~z = a - bi
abs(z) = r1 = sqrt(a*a + b*b)
sqrt(z) = sqrt(r1) * exp(i * t/2)
exp(z) = exp(a) * exp(i * b)
log(z) = log(r1) + i*t
sin(z) = 1/2i (exp(i * z1) - exp(-i * z))
cos(z) = 1/2 (exp(i * z1) + exp(-i * z))
atan2(y, x) = atan(y / x) # Minding the right quadrant, note the order.

atan2() 的复数参数所用的定义是

-i log((x + iy)/sqrt(x*x+y*y))

请注意,atan2(0, 0) 未定义。

以下额外操作支持实数和复数

Re(z) = a
Im(z) = b
arg(z) = t
abs(z) = r

cbrt(z) = z ** (1/3)
log10(z) = log(z) / log(10)
logn(z, n) = log(z) / log(n)

tan(z) = sin(z) / cos(z)

csc(z) = 1 / sin(z)
sec(z) = 1 / cos(z)
cot(z) = 1 / tan(z)

asin(z) = -i * log(i*z + sqrt(1-z*z))
acos(z) = -i * log(z + i*sqrt(1-z*z))
atan(z) = i/2 * log((i+z) / (i-z))

acsc(z) = asin(1 / z)
asec(z) = acos(1 / z)
acot(z) = atan(1 / z) = -i/2 * log((i+z) / (z-i))

sinh(z) = 1/2 (exp(z) - exp(-z))
cosh(z) = 1/2 (exp(z) + exp(-z))
tanh(z) = sinh(z) / cosh(z) = (exp(z) - exp(-z)) / (exp(z) + exp(-z))

csch(z) = 1 / sinh(z)
sech(z) = 1 / cosh(z)
coth(z) = 1 / tanh(z)

asinh(z) = log(z + sqrt(z*z+1))
acosh(z) = log(z + sqrt(z*z-1))
atanh(z) = 1/2 * log((1+z) / (1-z))

acsch(z) = asinh(1 / z)
asech(z) = acosh(1 / z)
acoth(z) = atanh(1 / z) = 1/2 * log((1+z) / (z-1))

argabslogcsccotacscacotcschcothacosechacotanh 分别有别名 rhothetalncoseccotanacosecacotancosechcotanhacosechacotanhReImargabsrhotheta 也可用作变异器。cbrt 仅返回一个解:如果您想要全部三个解,请使用 root 函数。

root 函数可用于计算某个复数的所有 n 个根,其中 n 是一个严格的正整数。有恰好 n 个这样的根,以列表形式返回。获取数学家称之为 j 的数字,使得

1 + j + j*j = 0;

只需编写

$j = (root(1, 3))[1];

z = [r,t] 的第 k 个根由下式给出

(root(z, n))[k] = r**(1/n) * exp(i * (t + 2*k*pi)/n)

您可以通过 root(z, n, k) 直接返回第 k 个根,索引从 开始,以 n - 1 结束。

还定义了 spaceship 数字比较运算符 <=>。为了确保其对实数的限制符合您的预期,比较首先在复数的实部上运行,并且仅当实部匹配时才比较虚部。

创建

要创建复数,请使用

$z = Math::Complex->make(3, 4);
$z = cplx(3, 4);

如果您知道该数字的笛卡尔形式,或

$z = 3 + 4*i;

如果您愿意。要使用极坐标形式创建数字,请使用

$z = Math::Complex->emake(5, pi/3);
$x = cplxe(5, pi/3);

代替。第一个参数是模数,第二个参数是角度(以弧度为单位,整圆是 2*pi)。(助记符:e 用作极坐标形式中复数的符号。)

可以写

$x = cplxe(-3, pi/4);

但它将被静默转换为 [3,-3pi/4],因为模数必须是非负的(它表示复平面上到原点的距离)。

还可以将复数作为 makeemakecplxcplxe 的任一参数:将使用参数的适当分量。

$z1 = cplx(-2,  1);
$z2 = cplx($z1, 4);

newmakeemakecplxcplxe 还将理解形式为

    	2-3i
    	-3i
	[2,3]
	[2,-3pi/4]
	[2]

在这种情况下,将从字符串中解析出适当的笛卡尔和指数分量,并用于创建新的复数。虚部和 theta 分别默认为零。

newmakeemakecplxcplxe 也将理解无参数的情况:这意味着纯零或 (0, 0)。

显示

打印时,复数通常以其笛卡尔样式 a+bi 显示,但在极坐标样式 [r,t] 更合适的情况下,这是合理的。将复数转换为可显示的字符串的过程称为字符串化

通过调用类方法 Math::Complex::display_format 并提供 "polar""cartesian" 作为参数,可以覆盖默认显示样式,该样式为 "cartesian"。不提供任何参数将返回当前设置。

可以通过调用 display_format 方法按每个数字覆盖此默认设置。与之前一样,不提供任何参数将返回此数字的当前显示样式。否则,您指定的任何内容都将成为特定数字的新显示样式。

例如

use Math::Complex;

Math::Complex::display_format('polar');
$j = (root(1, 3))[1];
print "j = $j\n";		# Prints "j = [1,2pi/3]"
$j->display_format('cartesian');
print "j = $j\n";		# Prints "j = -0.5+0.866025403784439i"

极坐标样式尝试强调诸如 k*pi/n(其中 n 是正整数,k 是 [-9, +9] 内的整数)之类的参数,这称为极坐标漂亮打印

有关字符串化的反向操作,请参阅 makeemake

在 PERL 5.6 中更改

现在可以使用参数哈希而不是仅仅一个参数来调用 display_format 类方法和相应的 display_format 对象方法。

可以使用 "style" 参数更改旧的显示格式样式,该样式可以具有值 "cartesian""polar"

$j->display_format(style => "polar");

一个参数的调用约定仍然有效。

$j->display_format("polar");

有两个新的显示参数。

第一个是 "format",它是一个 sprintf()-style 格式字符串,用于复数的两个数字部分。这在某种程度上取决于系统,但通常对应于 "%.15g"。您可以通过将 format 设置为 undef 来恢复为默认值。

# the $j from the above example

$j->display_format('format' => '%.5f');
print "j = $j\n";		# Prints "j = -0.50000+0.86603i"
$j->display_format('format' => undef);
print "j = $j\n";		# Prints "j = -0.5+0.86603i"

请注意,这也影响到 display_format 方法的返回值:在列表上下文中,将返回整个参数哈希,而不仅仅是样式参数值。如果您在列表上下文中调用 display_format 方法,则这与早期版本存在潜在的不兼容性。

第二个新的显示参数是 "polar_pretty_print",可以将其设置为 true 或 false,默认值为 true。有关其含义,请参见上一部分。

用法

由于重载,对复数进行算术处理非常简单,几乎是透明的。

以下是一些示例

use Math::Complex;

$j = cplxe(1, 2*pi/3);	# $j ** 3 == 1
print "j = $j, j**3 = ", $j ** 3, "\n";
print "1 + j + j**2 = ", 1 + $j + $j**2, "\n";

$z = -16 + 0*i;			# Force it to be a complex
print "sqrt($z) = ", sqrt($z), "\n";

$k = exp(i * 2*pi/3);
print "$j - $k = ", $j - $k, "\n";

$z->Re(3);			# Re, Im, arg, abs,
$j->arg(2);			# (the last two aka rho, theta)
				# can be used also as mutators.

常量

PI

如果单独导出,常量 pi 及其一些方便的倍数(pi2、pi4 和 pip2(pi/2)和 pip4(pi/4))也可用

use Math::Complex ':pi'; 
$third_of_circle = pi2 / 3;

Inf

浮点无穷大可以导出为子例程 Inf()

use Math::Complex qw(Inf sinh);
my $AlsoInf = Inf() + 42;
my $AnotherInf = sinh(1e42);
print "$AlsoInf is $AnotherInf\n" if $AlsoInf == $AnotherInf;

请注意,无穷大的字符串化形式在不同平台之间有所不同:例如,它可以是以下任何一个

inf
infinity
INF
1.#INF

或者它可以是其他内容。

还要注意,在某些平台上,尝试在算术运算中使用无穷大可能会导致 Perl 崩溃,因为使用无穷大会导致发送 SIGFPE 或其道德等价物。忽略此问题的方法是

local $SIG{FPE} = sub { };

由于零除或零对数引起的错误

除法 (/) 和以下函数

log	ln	log10	logn
tan	sec	csc	cot
atan	asec	acsc	acot
tanh	sech	csch	coth
atanh	asech	acsch	acoth

无法针对所有参数计算,因为这意味着除以零或取零的对数。这些情况会导致类似以下内容的致命运行时错误

cot(0): Division by zero.
(Because in the definition of cot(0), the divisor sin(0) is 0)
Died at ...

atanh(-1): Logarithm of zero.
Died at...

对于 csccotasecacscacotcschcothasechacsch,参数不能为 0(零)。对于对数函数和 atanhacoth,参数不能为 1(一)。对于 atanhacoth,参数不能为 -1(负一)。对于 atanacot,参数不能为 i(虚数单位)。对于 atanacoth,参数不能为 -i(负虚数单位)。对于 tansectanh,参数不能为 pi/2 + k * pi,其中 k 是任意整数。atan2(0, 0) 未定义,如果将复数参数用于 atan2(),当 z1**2+z2**2 == 0 时,将发生除以零的情况。

请注意,由于我们对实数近似值进行操作,因此当仅仅“非常接近”上述奇点时,可能会发生这些错误。

由于不可消化的参数导致的错误

makeemake 接受实数和复数参数。当它们无法识别参数时,它们将终止并显示以下错误消息

Math::Complex::make: Cannot take real part of ...
Math::Complex::make: Cannot take real part of ...
Math::Complex::emake: Cannot take rho of ...
Math::Complex::emake: Cannot take theta of ...

错误

声明 use Math::Complex; 会在调用者环境中导出许多数学例程,甚至会覆盖一些例程(sqrtlogatan2)。实际上,作者认为这是一个特性... ;-)

所有例程都希望给定实数或复数。不要尝试使用 BigFloat,因为 Perl 目前没有规则来消除两个重载实体之间的“+”操作(例如)。

在 Cray UNICOS 中,存在一些奇怪的数值不稳定性,导致 root()、cos()、sin()、cosh()、sinh() 快速丢失精度。请注意。该错误可能存在于 UNICOS 数学库、UNICOS C 编译器、Math::Complex 中。无论如何,它不会在 Perl 运行的任何其他地方表现出来。

另请参阅

Math::Trig

作者

Daniel S. Lewart <lewart!at!uiuc.edu>、Jarkko Hietaniemi <jhi!at!iki.fi>、Raphael Manfredi <Raphael_Manfredi!at!pobox.com>、Zefram <[email protected]>

许可证

此库是免费软件;您可以在与 Perl 本身相同的条款下重新分发和/或修改它。