在Python中实现复数比较?

时间:2014-06-20 19:08:06

标签: python numpy complex-numbers

我知道具有复数的比较运算符一般不能定义。这就是为什么python在尝试使用开箱即用的复杂比较时抛出TypeError异常的原因。我理解为什么会出现这种情况(请不要试图解释为什么不能比较两个复杂的数字)。

那就是说,在这种特殊情况下,我想根据它们的大小来实现复数比较。换句话说,对于z1z2复数值,z1 > z2 if-and-only-if abs(z1) > abs(z2),其中abs()实现复数幅度,如在numpy.abs()

我想出了一个解决方案(至少我认为我有)如下:

import numpy as np

class CustomComplex(complex):
    def __lt__(self, other):
        return np.abs(self) < np.abs(other)
    def __le__(self, other):
        return np.abs(self) <= np.abs(other)
    def __eq__(self, other):
        return np.abs(self) == np.abs(other)
    def __ne__(self, other):
        return np.abs(self) != np.abs(other)
    def __gt__(self, other):
        return np.abs(self) > np.abs(other)
    def __ge__(self, other):
        return np.abs(self) >= np.abs(other)

complex = CustomComplex

这似乎有效,但我有几个问题:

  1. 这是要走的路还是有更好的选择?
  2. 我希望我的包透明地使用内置的complex数据类型以及numpy.complex。如何在没有代码重复的情况下优雅地完成这项工作?

2 个答案:

答案 0 :(得分:5)

我担心我会偏离主题(是的,我完全阅读了你的帖子:-))。好的,Python允许你尝试比较复杂的数字,因为你可以单独定义所有运算符,即使我强烈建议你重新定义__eq__,就像你做的那样:你1 == -1

恕我直言,问题就在那里,并且会在某一时刻(或在任何使用你的包裹的人面前)涌现:当使用平等和不平等时,普通凡人(以及大多数蟒蛇代码)会做出简单的假设,例如{ {1}},-1 != 1隐含(a <= b) && (b <= a)。由于纯粹的数学原因,你根本无法同时将这两个假设成立。

另一个经典假设是a == b等同于a <= b。但是,预订-b <= -a相当于a <= b

话虽如此,我会尽力回答你的两个问题:

  • 1:恕我直言,这是一种有害的方式(如上所述),但我没有更好的选择......
  • 2:我认为mixin可能是一种限制代码重复的优雅方式

代码示例(基于您自己的代码,但未经过广泛测试):

-a <= -b

答案 1 :(得分:1)

根据您的要求,我会放弃为什么这可能是一个坏主意的所有原因。

  

这是要走的路还是有更好的选择?

当正常abs接受复数并且速度快得多时,无需使用numpy。如果你想减少代码(但这可能会更慢),那么total_ordering中的functools也可以很方便地进行这种简单的比较:

from functools import total_ordering
@total_ordering
class CustomComplex(complex):
    def __eq__(self, other):
        return abs(self) == abs(other)
    def __lt__(self, other):
        return abs(self) < abs(other)

(那是你需要的所有代码。)


  

我希望我的包能够透明地使用内置的复杂数据类型以及numpy.complex。如何在没有代码重复的情况下优雅地完成这项工作?

当右参数是正常的复数(或任何)数时,它会自动运行:

>>> CustomComplex(1+7j) < 2+8j
True

但如果你想使用运算符<等而不是函数,那么你可以做到最好。 complex类型不允许您设置__lt__并且TypeError是硬编码的。

如果要对普通complex数字进行此类比较,则必须定义并使用自己的比较函数而不是常规运算符。或者只使用abs(a) < abs(b),这是明确的,并不是非常冗长。


* Timing内置absnumpy.abs

>>> timeit.timeit('abs(7+6j)')
0.10257387161254883
>>> timeit.timeit('np.abs(7+6j)', 'import numpy as np')
1.6638610363006592