代码之家  ›  专栏  ›  技术社区  ›  danbroooks

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

  •  2
  • danbroooks  · 技术社区  · 7 年前

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

    在python中:

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

    在哈斯克尔:

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

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

    蟒蛇:

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

    哈斯克尔:

    > 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 回复  |  直到 7 年前
        1
  •  8
  •   Daniel Wagner    7 年前

    在哈斯克尔, (**) 对于 Complex 本质上是

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

    这就有很多机会出现严重的舍入错误。(我对Python了解不够,无法检查它对类似的log-then-exp表达式会做什么;我尝试过的东西抱怨它没有准备好处理 log(3j) .) 它有一系列特殊情况来阻止舍入错误,但没有一个检查完全真实的整数指数。您可能会认为这是一个bug或缺陷,并将其报告给 复杂的 键入作为另一个值得添加到实现中的特殊情况 (**) .

    同时,如果你知道指数是整数,你可以使用 (^) (仅适用于正数)或 (^^) 而是:

    Data.Complex> (0 :+ 3) ^ 2
    (-9.0) :+ 0.0
    
        2
  •  4
  •   senderle    7 年前

    虽然这两种语言给出的结果不同,但事实并非如此 非常 不同(如其他人在评论中所示)。因此,您可能会猜测,这只是一个略有不同的实现问题,您是对的。

    Daniel Wagner 表明在Haskell中 ** 运算符定义为

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

    Haskell做了一些特殊的套管,但大多数情况下,操作依赖于 exp log 对于复数。

    在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 是指数。 vabs at 给出 ,以便

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

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

    什么时候 b 是真的 if 块不执行,这将简化为 De Moivre's formula . 我找不到一个涵盖复杂或想象情况的规范公式,但它似乎很简单!