【问题标题】:Find if a point is inside a convex hull for a set of points without computing the hull itself查找一个点是否在一组点的凸包内而不计算包本身
【发布时间】:2011-06-21 13:53:19
【问题描述】:

测试点 P 是否在由一组点 X 形成的凸包内的最简单方法是什么?

我想要一种在高维空间(例如,最多 40 维)中工作的算法,它不会显式计算凸包本身。有什么想法吗?

【问题讨论】:

  • 您是否有特殊原因要这样做?计算凸包的成本不是很高(O(n lg n)),并且大大简化了问题。
  • @templatetypedef:在二维中计算凸包的成本不是很高。但随着维度数量的增加,它的成本会成倍增加。对于 40 维问题,您不想这样做。
  • 或许这个问题更适合mathoverflow?
  • @btilly- 啊,我的错误 - 我误读了维基百科页面。感谢您指出这一点!
  • 我看到您还没有接受答案。 user1071136 使用线性规划的答案是 the 答案(你不会找到更有效的方法)。

标签: algorithm graphics geometry computational-geometry


【解决方案1】:

当且仅当从该点到其他点的所有向量的方向都小于围绕它的圆/球/超球的一半时,该点才位于其他点的凸包之外。

这是两点情况的示意图,凸包内部的蓝色点(绿色)和外部的红色点:

对于红色的,圆存在二等分,因此从点到凸包上的点的向量仅与圆的一半相交。 对于蓝点,不可能找到这样的二等分。

【讨论】:

  • 这似乎是一个不错的O(m*n^2) 解决方案! +1
  • 第二个,这个属性检查m尺寸会很棘手。并不比求解m 线性方程组更容易。
  • 埃文斯,这样做是不够的。但是您可以这样做:将所有向量从该点求和到其他点。然后,使用该求和向量作为轴,从相关点再次投影所有向量。如果所有投影都位于轴的同一侧,则该点位于船体外部。我认为这是 O(m*n)
  • @allo:没关系,重要的是相对角度。
  • 我添加了一个情况的草图,如果你能检查它的正确性就好了。
【解决方案2】:

使用 scipy.optimize.minimize 测试一个点是否在船体空间中。

基于 user1071136 的回答。

如果你计算凸包,它确实会快很多,所以我为想要这样做的人添加了几行代码。我从 graham 扫描(仅限 2D)切换到 scipy qhull 算法。

scipy.optimize.minimize 文档:
https://docs.scipy.org/doc/scipy/reference/optimize.nonlin.html

import numpy as np
import scipy.optimize
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull


def hull_test(P, X, use_hull=True, verbose=True, hull_tolerance=1e-5, return_hull=True):
    if use_hull:
        hull = ConvexHull(X)
        X = X[hull.vertices]

    n_points = len(X)

    def F(x, X, P):
        return np.linalg.norm( np.dot( x.T, X ) - P )

    bnds = [[0, None]]*n_points # coefficients for each point must be > 0
    cons = ( {'type': 'eq', 'fun': lambda x: np.sum(x)-1} ) # Sum of coefficients must equal 1
    x0 = np.ones((n_points,1))/n_points # starting coefficients

    result = scipy.optimize.minimize(F, x0, args=(X, P), bounds=bnds, constraints=cons)

    if result.fun < hull_tolerance:
        hull_result = True
    else:
        hull_result = False

    if verbose:
        print( '# boundary points:', n_points)
        print( 'x.T * X - P:', F(result.x,X,P) )
        if hull_result: 
            print( 'Point P is in the hull space of X')
        else: 
            print( 'Point P is NOT in the hull space of X')

    if return_hull:
        return hull_result, X
    else:
        return hull_result

对一些样本数据进行测试:

n_dim = 3
n_points = 20
np.random.seed(0)

P = np.random.random(size=(1,n_dim))
X = np.random.random(size=(n_points,n_dim))

_, X_hull = hull_test(P, X, use_hull=True, hull_tolerance=1e-5, return_hull=True)

输出:

# boundary points: 14
x.T * X - P: 2.13984259782e-06
Point P is in the hull space of X

可视化它:

rows = max(1,n_dim-1)
cols = rows
plt.figure(figsize=(rows*3,cols*3))
for row in range(rows):
    for col in range(row, cols):
        col += 1
        plt.subplot(cols,rows,row*rows+col)
        plt.scatter(P[:,row],P[:,col],label='P',s=300)
        plt.scatter(X[:,row],X[:,col],label='X',alpha=0.5)
        plt.scatter(X_hull[:,row],X_hull[:,col],label='X_hull')
        plt.xlabel('x{}'.format(row))
        plt.ylabel('x{}'.format(col))
plt.tight_layout()

【讨论】:

    【解决方案3】:

    虽然最初的帖子是三年前的,但也许这个答案仍然会有所帮助。 Gilbert-Johnson-Keerthi (GJK) 算法找到两个凸多面体之间的最短距离,每个凸多面体都被定义为一组生成器的凸包——值得注意的是,凸包本身不需要计算。在一个特殊情况下,也就是被问及的情况,其中一个多面体只是一个点。为什么不尝试使用 GJK 算法来计算 P 和点 X 的凸包之间的距离?如果该距离为 0,则 P 在 X 内部(或至少在其边界上)。在 Octave/Matlab 中名为 ClosestPointInConvexPolytopeGJK.m 的 GJK 实现以及支持代码可在 http://www.99main.com/~centore/MunsellAndKubelkaMunkToolbox/MunsellAndKubelkaMunkToolbox.html 获得。 GJK 算法的简单描述可在 Sect 中找到。 2 篇论文,http://www.99main.com/~centore/ColourSciencePapers/GJKinConstrainedLeastSquares.pdf。我在 31 维空间中对一些非常小的集合 X 使用了 GJK 算法,并且取得了不错的效果。 GJK 的性能与其他人推荐的线性规划方法相比如何尚不确定(尽管任何比较都会很有趣)。 GJK 方法确实避免了计算凸包,或用线性不等式表示凸包,这两者都可能很耗时。希望这个答案有帮助。

    【讨论】:

      【解决方案4】:

      这个问题可以通过找到一个线性规划的可行点来解决。如果您对全部细节感兴趣,而不是仅仅将 LP 插入现有求解器,我建议您阅读 Boyd and Vandenberghe's excellent book on convex optimization 中的第 11.4 章。

      设置A = (X[1] X[2] ... X[n]),即第一列为v1,第二列为v2,以此类推

      解决以下 LP 问题,

      minimize (over x): 1
      s.t.     Ax = P
               x^T * [1] = 1
               x[i] >= 0  \forall i
      

      在哪里

      1. x^T 是 x 的转置
      2. [1] 是全1向量。

      如果该点在凸包中,则该问题有解决方案。

      【讨论】:

      • 有这方面的实现吗?我很难在代码中构建它。
      • 您使用哪种 LP 求解器? lpsolve.sourceforge.net/5.5 是一个开源 LP 求解器,使用起来非常简单。编辑:没有意识到你正在寻找整个shebang;不幸的是,我不知道有任何这样的包。
      • 我实际上是在浏览器中对此进行编程,所以我目前正在使用numericjs.com/documentation.html - 我只是在转换不等式时遇到了麻烦。 joyofdata.de/blog/… 这里有任何示例,但我也不熟悉 R,所以我仍然遇到问题!
      • 一旦我在 R 中找到了正确的包,并对函数进行了正确的修改,这个解决方案就是绝对的炸弹。曾经需要永远,甚至由于内存不足而失败的东西,现在因为没有计算凸包而变得瞬间完成。谢谢!
      • 周围的任何实现(尤其是在 Python 中都会很棒)。我不是程序员,我很难编写代码。
      【解决方案5】:

      我在 16 维时遇到了同样的问题。由于即使 qhull 也不能正常工作,因为必须生成太多的面,所以我通过测试开发了自己的方法,是否可以在新点和参考数据之间找到分离超平面(我称之为“HyperHull”;)) .

      寻找分离超平面的问题可以转化为凸二次规划问题(参见:SVM)。我在 python 中使用 cvxopt 执行此操作,代码行数少于 170 行(包括 I/O)。即使存在问题,该算法也无需修改任何维度即可工作,即维度越高,船体上的点数越高(参见:On the convex hull of random points in a polytope)。由于没有明确构造船体,而只是检查了一个点是否在内部,因此该算法在更高维度上具有非常大的优势,例如快速的船体。

      这种算法可以“自然地”并行化,并且加速应该等于处理器的数量。

      【讨论】:

      • 如果您可以将您的实现或其中的一部分放在 github 上,我相信很多人都会非常感激(mathworks.com/matlabcentral/answers/…)。也许对你来说这似乎很简单。
      【解决方案6】:

      您是否愿意接受通常应该有效但不能保证有效的启发式答案?如果你是,那么你可以试试这个随机的想法。

      令 f(x) 是到 P 的距离的立方乘以 X 中的事物数量,减去到 X 中所有点的距离的立方和。从某个地方随机开始,然后使用爬山在离 P 很远的球体中最大化 x 的 f(x) 的算法。除了退化的情况,如果 P 不在凸包中,这应该很有可能找到 P 所在的超平面的法线一侧,而 X 中的所有内容都在另一侧。

      【讨论】:

      • 您能否详细说明一下这种方法。特别是,您的目标函数中不存在 x,所以我不明白您的意思。
      • @tommsch 这个想法是在超球面上找到一个点 x' 远离 P 并靠近 X 中的事物。如果 P 在凸包之外,则点积向量从 P 到 x' 的向量从 P 到 X 中的每个 x_i 应该是正数。
      【解决方案7】:

      您不必计算凸包本身,因为它在多维空间中似乎很麻烦。有一个well-known property of convex hulls:

      在点[v1, v2, .., vn] 的凸包内的任何向量(点)v 都可以表示为sum(ki*vi),其中0 &lt;= ki &lt;= 1 和sum(ki) = 1。相应地,凸包之外的任何点都不会有这样的表示。

      在 m 维空间中,这将为我们提供具有 n 未知数的 m 线性方程组。

      编辑
      在一般情况下,我不确定这个新问题的复杂性,但对于m = 2,它似乎是线性的。也许,在这方面有更多经验的人会纠正我。

      【讨论】:

      • 实际上在 m 维空间中你只需要 m+1 个点,因为 Carathéodory 定理:en.wikipedia.org/wiki/… 困难在于找出哪个 m+1 个点起作用。
      • @lhf 这是一个很好的说明,尽管它不会影响答案的正确性(并且不清楚如何将其应用于求解这些方程)。
      • 问题是线性代数很容易为您的方程提供一个 n-m 维的解空间,但没有提供任何简单的方法来满足不等式。因此,您引用的定理是表明一个点在 m+1 个点的凸包内的好方法,但是对于更大的点集,您需要找到正确的 m+1 个点集以利用所述定理.有n个选择m+1个这样的集合来尝试。在 40 个维度中,这将是一个问题。
      • 这其实并不能解决问题。正如@btilly 提到的,线性方程组的解空间包含许多ki 为负数或大于1 的点。
      猜你喜欢
      • 2014-12-12
      • 2016-12-21
      • 1970-01-01
      • 2021-10-23
      • 2015-05-24
      • 2015-05-25
      • 2015-10-02
      • 2016-11-09
      • 2016-02-05
      相关资源
      最近更新 更多