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

.


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]]

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


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/


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


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.