Encontre o Casco Convexo de um conjunto de pontos 2D

20

Quando você martela um conjunto de pregos em uma placa de madeira e enrola um elástico em volta deles, você obtém um Casco Convexo .

insira a descrição da imagem aqui

Sua missão, se você decidir aceitá-la, é encontrar o casco convexo de um determinado conjunto de pontos 2D.


Algumas regras:

  • Escreva como uma função, as coordenadas da lista de pontos (em qualquer formato que você quiser) é o argumento
  • A saída deve ser a lista de pontos no casco convexo listados no sentido horário ou anti-horário, começando em qualquer um deles.
  • A lista de saída pode estar em qualquer formato razoável, onde as coordenadas de cada ponto são claramente distinguíveis. (Por exemplo, NÃO uma lista de uma dimensão {0.1, 1.3, 4, ...})
  • Se três ou mais pontos em um segmento do casco convexo estiverem alinhados, apenas os dois extremos devem ser mantidos na saída

Dados de amostra:

Amostra 0

Entrada:

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

Saída:

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

Gráficos do Mathematica (As figuras são apenas ilustrativas)

Amostra 1

Entrada:

{{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}}

Saída:

{{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}}

Gráficos do Mathematica

Amostra 2

Entrada:

{{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}}

Saída:

{{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}}

Gráficos do Mathematica

Aplicam-se as regras padrão de código de golfe. Nenhuma biblioteca de geometria ad-hoc. Código mais curto vence.

Editar 1

Estamos procurando uma resposta algorítmica aqui, não uma rotina pré-programada com localizador convexo de cascos como esse no MatLab ou no Mathematica

Editar 2

Resposta a comentários e informações adicionais:

  1. Você pode assumir que a lista de entrada contém o número mínimo de pontos que melhor lhe convier. Mas você deve garantir o tratamento adequado de (sub) conjuntos alinhados.
  2. Você pode encontrar pontos repetidos na lista de entrada
  3. O número máximo de pontos deve ser limitado apenas pela memória disponível
  4. Re "ponto flutuante": você precisa ser capaz de processar listas de entrada com coordenadas decimais como as fornecidas nas amostras. Você pode fazer isso usando uma representação de ponto flutuante

.

Dr. belisarius
fonte
2
Prevejo que o MATLAB vencerá este.
Paul R
Podemos assumir que existem pelo menos 3 pontos? Podemos assumir que os pontos são distintos? Somos obrigados a suportar coordenadas de ponto flutuante?
Peter Taylor
@PeterTaylor o exemplo indica a última resposta é verdade
John Dvorak
Podemos substituir a entrada?
John Dvorak
O problema com o tratamento consistente de pontos colineares é que existem problemas de arredondamento. Deveríamos ter permissão para cometer erros.
John Dvorak

Respostas:

2

Ruby, 168 caracteres

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}

Esse código ruby ​​também usa o algoritmo de embrulho para presente. A função Caceita uma matriz de pontos e retorna o casco convexo como matriz.

Exemplo:

>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
fonte
2

Mathematica 151

ainda trabalho em andamento

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

teste:

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

insira a descrição da imagem aqui

Dr. belisarius
fonte
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];$

Se a função não precisar ser acessível, remova f=para cortar mais dois caracteres.

Entrada / saída é uma única matriz de pontos, com cada ponto sendo definido pelas x,ypropriedades. A matriz de entrada é modificada e retornada (se a última não for necessária, remova os dois últimos caracteres).

A explicação pode ser adicionada mais tarde.

Conjunto de teste (não funcionará no oldIE):

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

ambiente de teste sugerido: http://coffeescript.org/

John Dvorak
fonte
Eu tentei com {{1, 1}, {2, 2}, {3, 3}, {1, 3}}e retornou [{"x" : 1, "y" : 1, "r" : 0}, {"x" : 1, "y" : 3, "r" : 0}, "x" : 2, "y" : 2, "r" : 0.78..}]enquanto eu acho que a resposta correta é alguma permutação de{{3, 3}, {1, 3}, {1, 1}}
Dr. belisarius
questão @belisarius com pontos colinear com a primeira, por vezes, produzindo casco incorreta fixo
John Dvorak
@belisarius, adicione isso como um caso de teste à pergunta.
John Dvorak
Parece que funciona corretamente agora :)
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

Usa um algoritmo de embrulho de presente. O resultado começa com o ponto mais à esquerda e quebra no sentido anti-horário.

Exemplo: h([(1, 1), (2, 2), (3, 3), (1, 3)])retorna[(1, 3), (1, 1), (3, 3)]

caixa de papelão
fonte
Você não precisa de um print para obter a saída?
Dr. belisarius
Eu pensei que por "saída" você quis dizer a saída da função. Deseja que a função imprima o resultado em vez de devolvê-lo?
cardboard_box
Eu pensei que exigir the output list can be in any reasonable format era clara o suficiente. Você acha que precisa ser explicitamente declarado?
Dr. belisarius
Parece que seus pontos de saída nem sempre correspondem aos de entrada se o ponto flutuante for usado. Por exemplo, h([(0, 1), (0,1), (0.1 , 1)])me dá[(0, 1), (0.10000000000000001, 1)]
Dr. belisarius 29/03