Trouver la coque convexe d'un ensemble de points 2D

20

Lorsque vous enfoncez un ensemble de clous dans une planche de bois et enroulez une bande de caoutchouc autour d'eux, vous obtenez une coque convexe .

entrez la description de l'image ici

Votre mission, si vous décidez de l'accepter, est de trouver la coque convexe d'un ensemble donné de points 2D.


Certaines règles:

  • Écrivez-le comme une fonction, les coordonnées de la liste du point (dans n'importe quel format que vous voulez) est l'argument
  • La sortie doit être la liste des points de la coque convexe répertoriés dans le sens horaire ou antihoraire, en commençant par l'un d'eux
  • La liste de sortie peut être dans n'importe quel format raisonnable où les coordonnées de chaque point peuvent être clairement distinguées. (Par exemple, PAS une liste unidimensionnelle {0,1, 1,3, 4, ...})
  • Si trois points ou plus dans un segment de la coque convexe sont alignés, seuls les deux extrêmes doivent être conservés sur la sortie

Exemples de données:

Échantillon 0

Contribution:

{{1, 1}, {2, 2}, {3, 3}, {1, 3}}

Production:

{{3, 3}, {1, 3}, {1, 1}}

Graphiques Mathematica (Les chiffres sont juste illustratifs)

Échantillon 1

Contribution:

{{4.4, 14}, {6.7, 15.25}, {6.9, 12.8}, {2.1, 11.1}, {9.5, 14.9}, 
 {13.2, 11.9}, {10.3, 12.3}, {6.8, 9.5}, {3.3, 7.7}, {0.6, 5.1}, {5.3, 2.4}, 
 {8.45, 4.7}, {11.5, 9.6}, {13.8, 7.3}, {12.9, 3.1}, {11, 1.1}}

Production:

{{13.8, 7.3}, {13.2, 11.9}, {9.5, 14.9}, {6.7, 15.25}, {4.4, 14}, 
 {2.1, 11.1}, {0.6, 5.1}, {5.3, 2.4}, {11, 1.1}, {12.9, 3.1}}

Graphiques Mathematica

Échantillon 2

Contribution:

{{1, 0}, {1, 1}, {1, -1}, {0.68957, 0.283647}, {0.909487, 0.644276}, 
 {0.0361877, 0.803816}, {0.583004, 0.91555}, {-0.748169, 0.210483}, 
 {-0.553528, -0.967036}, {0.316709, -0.153861}, {-0.79267, 0.585945},
 {-0.700164, -0.750994}, {0.452273, -0.604434}, {-0.79134, -0.249902}, 
 {-0.594918, -0.397574}, {-0.547371, -0.434041}, {0.958132, -0.499614}, 
 {0.039941, 0.0990732}, {-0.891471, -0.464943}, {0.513187, -0.457062}, 
 {-0.930053, 0.60341}, {0.656995, 0.854205}}

Production:

{{1, -1}, {1, 1}, {0.583004, 0.91555}, {0.0361877, 0.803816}, 
 {-0.930053, 0.60341}, {-0.891471, -0.464943}, {-0.700164, -0.750994}, 
 {-0.553528, -0.967036}}

Graphiques Mathematica

Les règles de code-golf standard s'appliquent. Pas de bibliothèques de géométrie ad hoc. Le code plus court gagne.

Modifier 1

Nous recherchons ici une réponse algorithmique, pas une routine préprogrammée de recherche de coque convexe comme celle-ci dans MatLab ou celle-ci dans Mathematica

Modifier 2

Répondre aux commentaires et informations supplémentaires:

  1. Vous pouvez supposer que la liste d'entrée contient le nombre minimum de points qui vous convient. Mais vous devez garantir un traitement approprié des (sous-) ensembles alignés.
  2. Vous pouvez trouver des points répétés dans la liste d'entrée
  3. Le nombre maximum de points ne doit être limité que par la mémoire disponible
  4. Re "virgule flottante": vous devez être capable de traiter des listes d'entrée avec des coordonnées décimales comme celles données dans les échantillons. Vous pouvez le faire en utilisant une représentation en virgule flottante

.

Dr. belisarius
la source
2
Je prédis que MATLAB gagnera celui-ci.
Paul R
Peut-on supposer qu'il y a au moins 3 points? Peut-on supposer que les points sont distincts? Sommes-nous tenus de prendre en charge les coordonnées à virgule flottante?
Peter Taylor
@PeterTaylor l'exemple indique que la dernière réponse est vraie
John Dvorak
Pouvons-nous écraser l'entrée?
John Dvorak
Le problème avec le traitement cohérent des points colinéaires est qu'il y a des problèmes d'arrondi. Nous devons être autorisés à faire des erreurs.
John Dvorak

Réponses:

2

Ruby, 168 caractères

C=->q{r=[]
f=m=q.sort[0]
t=-0.5
(_,_,t,*f=q.map{|x,y|a=x-f[0]
b=y-f[1]
[0==(d=a*a+b*b)?9:(-t+e=Math.atan2(b,a)/Math::PI)%2,-d,e,x,y]}.sort[0]
r<<=f)while
!r[1]||f!=m
r}

Ce code rubis utilise également l'algorithme d'emballage cadeau. La fonction Caccepte un tableau de points et renvoie la coque convexe en tant que tableau.

Exemple:

>p C[[[4.4, 14], [6.7, 15.25], [6.9, 12.8], [2.1, 11.1], [9.5, 14.9], 
     [13.2, 11.9], [10.3, 12.3], [6.8, 9.5], [3.3, 7.7], [0.6, 5.1], [5.3, 2.4], 
     [8.45, 4.7], [11.5, 9.6], [13.8, 7.3], [12.9, 3.1], [11, 1.1]]]

[[5.3, 2.4], [11, 1.1], [12.9, 3.1], [13.8, 7.3], [13.2, 11.9], [9.5, 14.9], [6.7, 15.25], [4.4, 14], [2.1, 11.1], [0.6, 5.1]]
Howard
la source
2

Mathematica 151

travail en cours

f = For[t = Sort@#; n = 1; l = Pi; a = ArcTan; c@1 = t[[1]],
       n < 2 || c@n != c@1, 
       n++,
      (l = a @@ (# - c@n); c[n + 1] = #) & @@
      t[[Ordering[Mod[a@## - l, 2 Pi] & @@ (#2 - #1) & @@@ Tuples@{{c@n}, t}, 1]]]] &

essai:

ClearAll[a, c, t];
s = {{1, 0}, {0.68957, 0.283647}, {0.909487, 0.644276}, {0.0361877, 0.803816}, 
     {0.583004, 0.91555}, {-0.748169, 0.210483}, {-0.553528, -0.967036}, 
     {0.316709, -0.153861}, {-0.79267, 0.585945}, {-0.700164, -0.750994}, 
     {0.452273, -0.604434}, {-0.79134, -0.249902}, {-0.594918, -0.397574}, 
     {-0.547371, -0.434041}, {0.958132, -0.499614}, {0.039941, 0.0990732}, 
     {-0.891471, -0.464943}, {0.513187, -0.457062}, {-0.930053, 0.60341}, 
     {0.656995, 0.854205}};
f@s
Show[Graphics@Line@Table[c@i, {i, n}], 
     ListPlot[{t, Table[c@i, {i, n}]}, 
     PlotStyle -> {PointSize[Medium], PointSize[Large]}, 
     PlotRange -> All]]

entrez la description de l'image ici

Dr. belisarius
la source
1

CoffeeScript, 276:

f=($)->z=$[0];e.r=Math.atan2(e.x-z.x,e.y-z.y)for e in $;$.sort((x,y)->(x.r>y.r)-(x.r<y.r));(loop(a=$[i-1]||$[$.length-1];b=$[i];c=$[i+1]||$[0];break if!b;s=(b.x-a.x)*(c.y-b.y)-(b.y-a.y)*(c.x-b.x);break if s<0||!s&&(a.x-b.x)*(b.x-c.x)<0;$.splice i,1))for i in [$.length-1..0];$

Si la fonction n'a pas besoin d'être accessible, supprimez-la f=pour raser deux autres caractères.

L'entrée / sortie est un tableau unique de points, chaque point étant défini par les x,ypropriétés. Le tableau d'entrée est modifié, ainsi que renvoyé (si ce dernier n'est pas requis, supprimez les deux derniers caractères).

Une explication peut être ajoutée ultérieurement.

Suite de tests (ne fonctionnera pas dans oldIE):

alert JSON.stringify f({x:e[0], y:e[1]} for e in JSON.parse "
{{1, 1}, {2, 2}, ...}
".replace(/{/g,"[").replace(/}/g,"]"))

environnement de test suggéré: http://coffeescript.org/

John Dvorak
la source
Je l'ai essayé avec {{1, 1}, {2, 2}, {3, 3}, {1, 3}}et il est revenu [{"x" : 1, "y" : 1, "r" : 0}, {"x" : 1, "y" : 3, "r" : 0}, "x" : 2, "y" : 2, "r" : 0.78..}]alors que je pense que la bonne réponse est une permutation de{{3, 3}, {1, 3}, {1, 1}}
Dr belisarius
@belisarius problème avec des points colinéaires avec le premier produisant parfois une coque incorrecte corrigée
John Dvorak
@belisarius veuillez l'ajouter comme cas de test à la question.
John Dvorak
Cela semble fonctionner correctement maintenant :)
Dr belisarius
1

Python, 209 205 195

from math import*
s=lambda(a,b),(c,d):atan2(d-b,c-a)
def h(l):
 r,t,p=[],pi/2,min(l)
 while 1:
    q=min(set(l)-{p},key=lambda q:(s(p,q)-t)%(2*pi));m=s(p,q);r+=[p]*(m!=t);p=q;t=m
    if p in r:return r

Utilise un algorithme d'emballage cadeau. Le résultat commence par le point le plus à gauche et passe dans le sens antihoraire.

Exemple: h([(1, 1), (2, 2), (3, 3), (1, 3)])retourne[(1, 3), (1, 1), (3, 3)]

boîte en carton
la source
Vous n'avez pas besoin d'un printpour obtenir la sortie?
Dr belisarius
Je pensais que par "sortie", vous vouliez dire la sortie de la fonction. Voulez-vous que la fonction imprime le résultat au lieu de le renvoyer?
cardboard_box
Je pensais que l'exigence the output list can be in any reasonable formatétait suffisamment claire. Pensez-vous que cela doit être explicitement déclaré?
Dr belisarius
Il semble que vos points de sortie ne correspondent pas toujours à ceux d'entrée si la virgule flottante est utilisée. Par exemple h([(0, 1), (0,1), (0.1 , 1)])me donne[(0, 1), (0.10000000000000001, 1)]
Dr. belisarius