web-dev-qa-db-fra.com

Trouver si un point est à l'intérieur d'une coque convexe pour un ensemble de points sans calculer la coque elle-même

Quel est le moyen le plus simple de tester si un point P est à l'intérieur d'une coque convexe formée par un ensemble de points X? 

J'aimerais un algorithme qui fonctionne dans un espace de grande dimension (jusqu'à 40 dimensions, par exemple) qui ne calcule pas explicitement la coque convexe elle-même. Des idées?

42
dimi

Le problème peut être résolu en trouvant un point réalisable d'un programme linéaire. Si vous êtes intéressé par tous les détails, au lieu de simplement brancher un LP dans un solveur existant, je vous recommande de lire le chapitre 11.4 in l'excellent livre de Boyd et Vandenberghe sur l'optimisation convexe .

Définissez A = (X[1] X[2] ... X[n]), c'est-à-dire que la première colonne est v1, la deuxième v2, etc.

Résoudre le problème de LP suivant,

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

où 

  1. x^T est la transposition de x
  2. [1] est le vecteur tout-1.

Le problème a une solution ssi le point est dans la coque convexe.

22
user1071136

Le point se trouve en dehors de la coque convexe des autres points si et seulement si la direction de tous les vecteurs allant de celui-ci à ces autres points est sur moins de la moitié d'un cercle/sphère/hypersphère autour de lui.

Voici un croquis pour la situation de deux points, un bleu à l'intérieur de la coque convexe (vert) et le rouge à l'extérieur:

 Sketch of the points

Pour le rouge, il existe des bissections du cercle, de sorte que les vecteurs du point aux points de la coque convexe ne coupent que la moitié du cercle . Pour le point bleu, il est impossible de trouver un tel bissection.

20
Svante

Vous n'avez pas à calculer la coque convexe elle-même, car cela semble assez gênant dans les espaces multidimensionnels. Il existe une propriété bien connue des coques convexes :

Tout vecteur (point) v à l'intérieur de la coque convexe des points [v1, v2, .., vn] peut être présenté sous la forme sum(ki*vi), où 0 <= ki <= 1 et sum(ki) = 1. De manière correspondante, aucun point en dehors de la coque convexe n'aura une telle représentation. 

Dans l'espace m-dimensionnel, cela nous donnera l'ensemble d'équations linéaires m à n inconnues.

modifier
Je ne suis pas sûr de la complexité de ce nouveau problème en général, mais pour m = 2, il semble linéaire. Peut-être que quelqu'un avec plus d'expérience dans ce domaine me corrigera. 

9
Nikita Rybak

Bien que le poste original date de trois ans, cette réponse sera peut-être toujours utile. L'algorithme Gilbert-Johnson-Keerthi (GJK) trouve la distance la plus courte entre deux polytopes convexes, chacun d'eux étant défini comme l'enveloppe convexe d'un ensemble de générateurs - notamment, l'enveloppe convexe elle-même n'a pas à être calculée. Dans un cas particulier, celui sur lequel on vous a posé la question, l'un des polytopes n'est qu'un point. Pourquoi ne pas essayer d’utiliser l’algorithme GJK pour calculer la distance entre P et la coque convexe des points X? Si cette distance est 0, alors P est à l'intérieur de X (ou au moins sur sa limite). Une implémentation de GJK dans Octave/Matlab, appelée ClosestPointInConvexPolytopeGJK.m, ainsi que le code associé, est disponible à l'adresse http://www.99main.com/~centore/MunsellAndKubelkaMunkToolbox/MunsellAndKubelkaMunkToolbox.html . Une description simple de l'algorithme GJK est disponible dans Sect. 2 d'un article, à http://www.99main.com/~centore/ColourSciencePapers/GJKinConstrainedLeastSquares.pdf . J'ai utilisé l'algorithme GJK pour de très petits ensembles X dans un espace à 31 dimensions et j'ai obtenu de bons résultats. La performance de GJK par rapport aux méthodes de programmation linéaires recommandées par d’autres est incertaine (bien que toute comparaison puisse être intéressante). La méthode GJK évite de calculer la coque convexe ou d’exprimer la coque en termes d’inégalités linéaires, qui peuvent prendre beaucoup de temps. J'espère que cette réponse aide.

2
Paul Centore

J'ai eu le même problème avec 16 dimensions. Puisque même qhull ne fonctionnait pas correctement car trop de visages devaient être générés, j’ai développé ma propre approche en testant si un hyperplan séparateur peut être trouvé entre le nouveau point et les données de référence (j’appelle cela "HyperHull";)) . 

Le problème de trouver un hyperplan séparateur peut être transformé en un problème de programmation convexe quadratique (voir: SVM ). Je l'ai fait en python en utilisant cvxopt avec moins de 170 lignes de code (incluant les E/S). L'algorithme fonctionne sans modification dans aucune dimension, même s'il existe un problème, à savoir que la dimension est supérieure au nombre de points sur la coque (voir: Sur la coque convexe de points aléatoires dans un polytope ). Etant donné que la coque n'est pas explicitement construite mais uniquement vérifiée, qu'un point soit à l'intérieur ou non, l'algorithme présente de très grands avantages dans les dimensions supérieures par rapport à p. Ex. coque rapide.

Cet algorithme peut être «naturellement» parallélisé et la vitesse devrait être égale au nombre de processeurs.

2
Michael Hecht

Êtes-vous disposé à accepter une réponse heuristique qui devrait normalement fonctionner, mais n’est pas garantie? Si vous êtes alors vous pouvez essayer cette idée aléatoire.

Soit f(x) le cube de la distance à P fois le nombre de choses dans X, moins la somme des cubes de la distance à tous les points de X. Commencez quelque part au hasard et utilisez une colline algorithme d’escalade pour maximiser f(x) pour x dans une sphère très éloignée de P. Sauf dans les cas dégénérés, si P n’est pas dans la coque convexe, la probabilité de retrouver la normale à un hyperplan dont P est d’un côté et tout dans X est de l’autre.

1
btilly

Une rédaction pour tester si un point se trouve dans un espace coque, en utilisant scipy.optimize.minimize.

Basé sur la réponse de user1071136.

Si vous calculez la coque convexe, cela va beaucoup plus vite. J'ai donc ajouté quelques lignes aux personnes qui souhaitent le faire. Je suis passé de graham scan (2D uniquement) à l'algorithme Scipy Qhull. 

scipy.optimize.minimize documentation:
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

Testez sur des exemples de données:

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)

Sortie:

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

Visualisez le:

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()

 Visualization of hull test

1
cnash