如何有效地比较两组无序向量(`np.ndarray`)?

2024-10-06 12:06:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图把我的问题弄清楚。这篇文章的结尾仍然可以找到老的离题

问题:我有一个{}由3个{}(np.ndarray)的{}点组成的三维矩阵。如果这些点作为无序集是平稳的,我们说它们相对于变换R(3×3矩阵)是对称的

这意味着AA @ R.T相差1。最多一个排列和两个。在对排列进行校正后,两个矩阵可能会有一个数值公差参数不同:np.allclose(A, permuted(A @ R.T)) == True(我事先不知道permuted(),这肯定取决于R

问题:如何创建以下函数:

def is_symmetric(A, R, atol=1e-5) -> bool:
    # checks symmetry as defined above, considering both numerical noise
    # and permutation of vectors.

(关于可能的方法以及我的疑问和尝试的一些讨论见下文。)


旧题外话

我想检查以向量表示的点集合中的对称性。这意味着检查这些点在空间中应用矩阵变换时是否不变,例如旋转或平面镜像

import numpy as np
R = np.array([[0, -1, 0],  # 90° rotation around z as an example
              [1,  0, 0],
              [0,  0, 1]])

关键是,我接受向量的排列::只要某个初始位置被转换到另一个先前存在的位置,我就没事。这意味着检查从一个向量到另一个向量的变换对

天真的解决方案将在A @ R.T的行上循环(其中A是一个矩阵,其行是点位置),并尝试为A的每个初始行匹配一个变换向量,该向量似乎随列数的平方增长

另一种可能是对向量进行预排序(例如,通过它们的坐标值):

A = np.array([[1,   0,   0],  # some points
              [0,   1,   0],
              [0,   0,   1],
              [0.5, 0.5, 0]])
A = np.array(sorted(A, key=lambda v: (v[2], v[1], v[0])))  # sort by z, y, x values
# array([[1. , 0. , 0. ],
#        [0.5, 0.5, 0. ],
#        [0. , 1. , 0. ],
#        [0. , 0. , 1. ]])

A_rotated = np.array(sorted(A @ R.T, key=lambda v: (v[2], v[1], v[0])))
# array([[-1. ,  0. ,  0. ],   # no match
#        [-0.5,  0.5,  0. ],   # no match
#        [ 0. ,  1. ,  0. ],   # match
#        [ 0. ,  0. ,  1. ]])  # match

(这种方法有两种排序,所以O(n ln(n))?) 第三个想法是比较从原始向量和旋转向量创建的集合。我有一种直觉,这就像比较排序向量一样好

但还有一件事:如何处理近似比较?我想接受两个向量vw相等,如果是np.allclose(v, w) == True或等效(即abs(v - w) < eps或类似):

np.allclose([1, 0, 0], [1, 0, 0])
# True
np.allclose([1, 0, 0], [1 + 1e-5, 0, 0], atol=1e-5)
# True
np.allclose([1, 0, 0], [1 + 1e-4, 0, 0], atol=1e-5)
# False

所以这里有一个问题:如何(有效地)比较两组(无序)向量的相等性,同时考虑数值近似(例如np.allclose


Tags: 方法true排序asmatchnp矩阵array
2条回答

很简单,您可以将向量的所有值<atol转换为0。您可以使用numpy.vectorize,查找文档here

import numpy as np

def transform(x, atol):
    if x>0 and x-1<atol:
        return 1
    else:
        return x

myarray = np.array([[1, 0, 0], [1 + 1e-4, 0, 0]])
vf = np.vectorize(transform)
new_array = vf(myarray, atol=1e-5)

现在您可以进行符号验证

如果我回答了你的问题,请告诉我

更新

我看到您已经在numpy中有了函数isclose(),您应该使用它,而不是从头开始使用我的函数

不过,我不会删除我的答复,以便保留与之有关的评论

下面是一个使用np.lexsort的函数:

def is_symmetric(A, R, *args, **kwargs):
    A = np.asanyarray(A)
    A = A[np.lexsort(A.T)]

    A_t = A @ np.asanyarray(R).T
    A_t = A_t[np.lexsort(A_t.T)]
    return np.allclose(A, A_t, *args, **kwargs)

一些结果:

R = np.array([[0, -1, 0],  # 90° rotation as an example
              [1,  0, 0],
              [0,  0, 1]])

is_symmetric([[0, 0, 0]], R)
# True
is_symmetric([[1, 0, 0],
              [0, 1, 0],
              [0, 0, 0]], R)
# False
is_symmetric([[1, 0, 0],
              [0, 1, 0],
              [0, 0, 0],
              [-1, 0, 0]], R)
# False
is_symmetric([[1, 0, 0],
              [0, 1, 0],
              [0, 0, 0],
              [-1, 0, 0],
              [0, -1, 0]], R)
# True

对于100000个随机向量,性能似乎很好:

A = np.random.rand(100000, 3)
%timeit is_symmetric(A, R)
# 82.2 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

相关问题 更多 >