我试图将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
为什么会这样?
答案 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做了一些特殊的大小写,但大部分时间,操作都依赖于复数的exp
和log
的通用定义。
在Python中,它有点不同:使用极表示来计算功率。这种方法涉及使用一组不同的通用函数 - 大多数是基本的三角函数而不是普通的浮点数 - 几乎没有特殊的套管。我不清楚这种方法总体上是否更好,但它确实会在您选择的特定情况下给出更正确的答案。
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
是指数。 vabs
和at
给出了a
的极坐标,以便
a.real = vabs * cos(at)
a.imag = vabs * sin(at)
正如您在最后两行代码中所看到的,len
和phase
给出了结果的相应极坐标表示r
。
当b
为真时,if
块不会被执行,这会简化为De Moivre's formula。我找不到涵盖复杂或虚构案例的规范公式,但看起来很简单!