【问题标题】:Approximate equality of unordered set of complex floats无序复数浮点数的近似相等
【发布时间】:2014-01-21 20:32:24
【问题描述】:

我想对无序的复杂浮点数的 numpy 数组进行鼻子测试。

例如,如果

a = [1+1j, 1-1j, 2+2j, 2-2j, 2+2j, 2-2j]

b = [2+2j, 2-2j, 1+1j, 1.000000000000001-1j, 2+2j, 2-2j]

断言应该成功,因为它们具有大致相同的值相同的次数。顺序无关紧要。

对于常规浮点数,assert_array_almost_equal(np.sort(a), np.sort(b)) 可以,但在这里不起作用,因为它先按实部排序,然后按虚部排序,因此由于浮点错误,它们被排序为:

a: [ 1.-1.j,  1.+1.j,  2.-2.j,  2.-2.j,  2.+2.j,  2.+2.j]    
b: [ 1.+1.j,  1.-1.j,  2.-2.j,  2.-2.j,  2.+2.j,  2.+2.j]

有没有内置的方法来做到这一点?如果没有,我想我可以诉诸 cplxreal 之类的东西,但这似乎需要添加到测试文件中。

【问题讨论】:

  • 如果您尝试严格遵循结果规范,这似乎是 NP 完全的,但如果您对在疯狂情况下变得有点模糊的结果感到满意,也不会太难。
  • 首先想到的算法是某种贪婪的最近邻匹配,您可以将一个集合中的每个元素连续匹配到另一个集合中最近的未匹配邻居,当您用完或最近的邻居太远了。看起来可能会有更有效的东西,但无论如何,它不会像排序那么简单。
  • 您可以尝试消除浮点错误,例如,截断到 x 个有效数字(其中 x 由您的应用程序所需的精度确定),然后进行比较。我怀疑这将是一个非常有效的想法,并且不是一个内置的解决方案。

标签: python numpy floating-point equality approximate


【解决方案1】:

如何按大小对数组进行排序?

def foo(a):
    return a[np.argsort(a*a.conjugate())]
np.testing.assert_array_almost_equal(foo(a),foo(b))

【讨论】:

  • 不够好,因为我们错误地将[-.9, 1.1][.9, -1.1] 的区别视为[1.8, 2.2]。但是在按大小排序之后,您可以更有效地迭代两个数组,所以这是一个很好的第一步。
  • nose 测试的上下文中,您可能不需要考虑所有可能的排序。只需选择具有不同大小的参考值。目标是使排序不受小数点后第 n 位小错误的影响。
  • 多哈。我刚刚意识到按虚部排序可能适用于我所有的测试。量级是行不通的。角度可以工作一半。也许将此答案编辑为“如果您知道数字的形状,请使用角度或大小或虚部对它们进行排序”。在一般情况下不起作用,但如果您已经知道答案,则可以。
  • 是的,我又看了一遍是你的例子。这些值的角度差异大于幅度。但要注意角度的周期性 (0==2*pi)
  • @hpaulj:我的例子更适用于一般情况。我的实际应用程序以可预测的方式排列它们。所以我目前使用assert_allclose(sorted(z, key=np.imag), sorted(z2, key=np.imag))assert_allclose(sorted(z, key=np.angle), sorted(z2, key=np.angle)),具体取决于点在复平面上的排列方式。
【解决方案2】:

我不确定在最坏的情况下是否有比O(n^2) 更好的方法,但如果您可以接受,您可以复制其中一个列表并使用修改后的equals 函数进行消除看看他们是否匹配。

def equals(a, b, tolerance):
    return abs(a-b) < tolerance

然后遍历您的列表,在找到匹配项时删除它们

def equivalent_lists(a, b, tolerance):
    new_b = b[:]
    for a_item in a:
        truths = [equals(a_item, b_item, tolerance) for b_item in new_b]
        if not any(truths):
            return False
        else:
            new_b.pop(truths.index(True))
    return not bool(new_b)

似乎在您的初始情况下有效,至少以粗略的方式:

a = [1+1j, 1-1j, 2+2j, 2-2j, 2+2j, 2-2j]
b = [2+2j, 2-2j, 1+1j, 1.000000000000001-1j, 2+2j, 2-2j]
c = [2+2j, 2-2j, 1+1j, 2-1j, 2+2j, 2-2j]

equivalent_lists(a, b, 0.0001)
>>> True
equivalent_lists(a, c, 0.0001)
>>> False

不是最漂亮的解决方案,但至少似乎以一种非常透明的方式工作。

【讨论】:

  • 我不关心效率,我关心的是用尽可能少的额外代码进行测试
  • @endolith 这还不够短吗?我可以让它更简洁。
  • 嗯。现在我觉得我应该把它分成 2 个问题,一个用于一般情况(cplxpair),另一个用于快速单元测试。
【解决方案3】:

另一种方法可能是考虑二维空间中的点。 a 是一组点。 b 是另一组。 b 中的每个点都应该接近a 中的一个点。

diff = np.array(a)[None,:]-np.array(b)[:,None]
X = np.round(diff*diff.conjugate())==0  # round to desired error tollerance
X

array([[False, False,  True, False,  True, False],
       [False, False, False,  True, False,  True],
       [ True, False, False, False, False, False],
       [False,  True, False, False, False, False],
       [False, False,  True, False,  True, False],
       [False, False, False,  True, False,  True]], dtype=bool)

下一步是测试X。如果a(和b)中的值不同,则每一列和每一行都应该有一个True。但是这里有重复。

In [29]: I,J=np.where(X)

In [30]: I
Out[30]: array([0, 0, 1, 1, 2, 3, 4, 4, 5, 5])

In [31]: J
Out[31]: array([2, 4, 3, 5, 0, 1, 2, 4, 3, 5])

set(I)set(J) 都是 [0,1,..5],所以所有的行和列都有一个匹配项。这套测试可能就足够了,即使有重复。我必须探索点和不匹配的其他组合。

这个答案不完整,但可能会提供一些有用的想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    • 2020-07-31
    • 2015-07-08
    相关资源
    最近更新 更多