Haskell和Python中复数实现之间的差异

时间:2018-01-04 17:45:24

标签: python haskell complex-numbers

我试图将Python中的复数功能映射到Haskell中的Data.Complex,但我已经达到了他们不同的地步,我不确定为什么。

在python中:

>>> x = 3j
3j
>>> x.real
0.0
>>> x.imag
3.0

在Haskell:

> import Data.Complex
> let j n = 0 :+ n
> let x = j 3.0
> realPart x
0.0
> imagPart x
3.0

到目前为止他们看起来一样。看起来对它们进行操作并没有太大区别:

的Python:

>>> y = 1 + x
(1+3j)
>>> y.real
1.0
>>> y.imag
3.0

Haskell中:

> let y = 1 + x
> realPart y
1.0
> imagPart y
3.0

孤立+ - * / **所有人似乎都以同样的方式行事。但是,此操作会产生两种不同的结果:

>>> z = (y - 1) ** 2
(-9+0j)
>>> z.real
-9.0
>>> z.imag
0.0

但在哈斯克尔:

> let z = (y - 1) ** 2
> realPart z
-9.000000000000002
> imagPart z
1.1021821192326181e-15

为什么会这样?

2 个答案:

答案 0 :(得分:8)

在Haskell中,(**)的{​​{1}}基本上是

Complex

它有很多机会让错误的舍入错误进入。(我不知道足够的Python来检查它会用类似的log-then-exp表达式做什么;我试过的东西抱怨它不是&# 39; t准备好处理a ** b = exp (b * log a) 。)它有一堆特殊情况来阻止舍入错误,但没有检查完全实数整数指数。您可能会将此视为错误或不完整,并将其报告给负责log(3j)类型的人员,作为另一个值得添加到Complex实施的特殊情况。

与此同时,如果你知道你的指数是不可或的,你可以使用(**)(仅限正数)或(^)代替:

(^^)

答案 1 :(得分:4)

尽管两种语言给出的结果不同,但它们并非非常不同(正如其他语言在评论中所指出的那样)。所以你可能会猜测这只是一个稍微不同的实现问题 - 你是对的。

Daniel Wagner表示在Haskell中,**运算符被定义为

a ** b = exp (b * log a)

Haskell做了一些特殊的大小写,但大部分时间,操作都依赖于复数的explog的通用定义。

在Python中,它有点不同:使用表示来计算功率。这种方法涉及使用一组不同的通用函数 - 大多数是基本的三角函数而不是普通的浮点数 - 几乎没有特殊的套管。我不清楚这种方法总体上是否更好,但它确实会在您选择的特定情况下给出更正确的答案。

这是core of the implementation

vabs = hypot(a.real,a.imag);
len = pow(vabs,b.real);
at = atan2(a.imag, a.real);
phase = at*b.real;
if (b.imag != 0.0) {
    len /= exp(at*b.imag);
    phase += b.imag*log(vabs);
}
r.real = len*cos(phase);
r.imag = len*sin(phase);

此处,a是基数,b是指数。 vabsat给出了a的极坐标,以便

a.real = vabs * cos(at)
a.imag = vabs * sin(at)

正如您在最后两行代码中所看到的,lenphase给出了结果的相应极坐标表示r

b为真时,if块不会被执行,这会简化为De Moivre's formula。我找不到涵盖复杂或虚构案例的规范公式,但看起来很简单!