ポイントPがポイントXのセットによって形成された凸包の内側にあるかどうかをテストする最も簡単な方法は何ですか?
凸包自体を明示的に計算しない高次元空間(たとえば、最大40次元)で動作するアルゴリズムが欲しいのですが。何か案は?
この問題は、線形計画の実行可能な点を見つけることで解決できます。 LPを既存のソルバーに単にプラグインするのではなく、詳細に興味がある場合は、第11.4章 Boyd and Vandenbergheの凸最適化に関する優れた本 を読むことをお勧めします。
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
どこ
x^T
はx
の転置です[1]
はすべて1のベクトルです。問題は、点が凸包内にある場合の解決策です。
多次元空間ではかなり厄介なように見えるので、凸包自体を計算する必要はありません。 凸包のよく知られた特性 があります:
点の凸包内の任意のベクトル(点)v
_[v1, v2, .., vn]
_は、sum(ki*vi)
として表すことができます。ここで、_0 <= ki <= 1
_およびsum(ki) = 1
です。これに対応して、凸包の外側の点にはそのような表現はありません。
M次元空間では、これによりm
未知数を含むn
線形方程式のセットが得られます。
edit
この新しい問題の一般的な場合の複雑さはわかりませんが、_m = 2
_の場合は線形に見えます。おそらく、この分野でより多くの経験を持つ誰かが私を正してくれるでしょう。
元の投稿は3年前でしたが、おそらくこの答えはまだ役に立ちます。 Gilbert-Johnson-Keerthi(GJK)アルゴリズムは、2つの凸型ポリトープ間の最短距離を検出します。これらのそれぞれは、ジェネレーターのセットの凸型ハルとして定義されます。特に、凸型ハル自体を計算する必要はありません。質問されているような特別なケースでは、ポリトープの1つは単なるポイントです。 GJKアルゴリズムを使用して、Pと点Xの凸包の間の距離を計算してみませんか?その距離が0の場合、PはX内(または少なくともその境界上)にあります。 ClosestPointInConvexPolytopeGJK.mと呼ばれるOctave/MatlabのGJK実装は、サポートコードとともに http://www.99main.com/~centore/MunsellAndKubelkaMunkToolbox/MunsellAndKubelkaMunkToolbox.html で入手できます。 GJKアルゴリズムの簡単な説明がSectにあります。 http://www.99main.com/~centore/ColourSciencePapers/GJKinConstrainedLeastSquares.pdf の論文の2 31次元空間のいくつかの非常に小さなセットXにGJKアルゴリズムを使用して、良い結果が得られました。 GJKのパフォーマンスが、他の人が推奨している線形計画法とどのように比較されるかは不確かです(比較は興味深いものですが)。 GJKメソッドは、凸包の計算、または線形不等式での包の表現を避けますが、どちらも時間がかかる場合があります。この回答がお役に立てば幸いです。
16次元でも同じ問題がありました。生成しなければならない面が多すぎるため、qhullも適切に機能しなかったため、新しい点と参照データの間に分離超平面が見つかるかどうかをテストして、独自のアプローチを開発しました(これを「HyperHull」と呼びます;)) 。
分離超平面を見つける問題は、凸2次計画問題に変換できます( [〜#〜] svm [〜#〜] を参照)。私はこれをpythonでcvxoptを使用して170行未満のコード(I/Oを含む)で実行しました。問題が存在する場合でも、アルゴリズムはどの次元でも変更せずに機能します。船体上のポイントの数が多いほど(参照: ポリトープ内のランダムポイントの凸包上 )。ハルは明示的に構築されておらず、チェックされるだけなので、ポイントが内部にあるかどうか、このアルゴリズムは、高速船体などと比較して、高次元で非常に大きな利点があります。
このアルゴリズムは「自然に」並列化することができ、スピードアップはプロセッサの数と同等でなければなりません。
通常は機能するが保証はされないヒューリスティックな回答を受け入れてもよろしいですか?もしそうなら、このランダムなアイデアを試すことができます。
f(x)をPまでの距離の立方体にXの物数を掛けたものから、Xのすべての点までの距離の立方体の合計を差し引いたものとします。 、そして山登りアルゴリズムを使用して、Pから非常に遠い球のxのf(x)を最大化します。縮退の場合を除いて、Pが凸包にない場合、これはPが片側にあり、Xのすべてが反対側にある超平面の法線を見つける確率は非常に高いです。
User1071136の回答に基づく。
凸包を計算すると、はるかに高速になります。そのため、それを実行したい人のために2、3行追加しました。グラハムスキャン(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()