Temukan Convex Hull dari sekumpulan poin 2D

20

Saat Anda memalu satu set paku ke papan kayu dan membungkus karet gelang di sekitarnya, Anda mendapatkan Convex Hull .

masukkan deskripsi gambar di sini

Misi Anda, jika Anda memutuskan untuk menerimanya, adalah menemukan Convex Hull dari sekumpulan poin 2D.


Beberapa peraturan:

  • Tuliskan sebagai fungsi, daftar titik terkoordinasi (dalam format apa pun yang Anda inginkan) adalah argumennya
  • Keluaran harus berupa daftar titik-titik dalam cembung yang terdaftar searah atau berlawanan arah jarum jam, mulai dari salah satu dari mereka
  • Daftar keluaran dapat dalam format yang masuk akal di mana koordinat setiap titik dapat dibedakan dengan jelas. (Misalnya BUKAN satu daftar redup {0,1, 1,3, 4, ...})
  • Jika tiga atau lebih titik di segmen cembung cembung diselaraskan, hanya dua ekstrem yang harus disimpan pada output

Contoh data:

Contoh 0

Memasukkan:

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

Keluaran:

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

Grafik Mathematica (Angka-angka itu hanya ilustrasi)

Contoh 1

Memasukkan:

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

Keluaran:

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

Grafik Mathematica

Contoh 2

Memasukkan:

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

Keluaran:

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

Grafik Mathematica

Aturan standar kode-golf berlaku. Tidak ada perpustakaan geometri ad-hoc. Kode lebih pendek menang.

Edit 1

Kami mencari jawaban algoritmik di sini, bukan rutin pra-terprogram cembung finder yang diprogram seperti ini di MatLab atau yang ini di Mathematica

Edit 2

Menjawab komentar dan info tambahan:

  1. Anda dapat mengasumsikan daftar input berisi jumlah minimum poin yang cocok untuk Anda. Tetapi Anda harus memastikan perlakuan yang tepat untuk set (sub) yang selaras.
  2. Anda mungkin menemukan poin berulang dalam daftar input
  3. Jumlah maksimum poin harus dibatasi hanya oleh memori yang tersedia
  4. Re "floating point": Anda harus dapat memproses daftar input dengan koordinat desimal seperti yang diberikan dalam sampel. Anda bisa melakukannya dengan menggunakan representasi floating point

.

Belisarius
sumber
2
Saya memprediksi bahwa MATLAB akan memenangkan yang satu ini.
Paul R
Bisakah kita berasumsi bahwa setidaknya ada 3 poin? Bisakah kita mengasumsikan bahwa poinnya berbeda? Apakah kita harus mendukung koordinat floating point?
Peter Taylor
@PeterTaylor contoh menunjukkan jawaban terakhir benar
John Dvorak
Bisakah kita menimpa inputnya?
John Dvorak
Masalah dengan memperlakukan titik collinear secara konsisten adalah ada masalah pembulatan. Kita harus diizinkan melakukan kesalahan.
John Dvorak

Jawaban:

2

Ruby, 168 karakter

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}

Kode ruby ​​ini juga menggunakan algoritma pembungkusan hadiah. Fungsi Cmenerima array titik dan mengembalikan lambung cembung sebagai array.

Contoh:

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

Mathematica 151

masih bekerja dalam proses

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

pengujian:

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

masukkan deskripsi gambar di sini

Belisarius
sumber
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];$

Jika fungsi tidak perlu diakses, hapus f=untuk mencukur dua karakter lagi.

Input / output adalah array titik tunggal, dengan masing-masing titik ditentukan oleh x,yproperti. Array input diubah, serta dikembalikan (jika yang terakhir tidak diperlukan, hapus dua karakter terakhir).

Penjelasan dapat ditambahkan nanti.

Test suite (tidak akan berfungsi di oldIE):

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

lingkungan pengujian yang disarankan: http://coffeescript.org/

John Dvorak
sumber
Saya mencobanya {{1, 1}, {2, 2}, {3, 3}, {1, 3}}dan mengembalikannya [{"x" : 1, "y" : 1, "r" : 0}, {"x" : 1, "y" : 3, "r" : 0}, "x" : 2, "y" : 2, "r" : 0.78..}]sementara saya pikir jawaban yang benar adalah permutasi dari{{3, 3}, {1, 3}, {1, 1}}
Dr. belisarius
@belisarius masalah dengan poin collinear dengan yang pertama terkadang menghasilkan kesalahan lambung yang diperbaiki
John Dvorak
@belisarius tolong tambahkan ini sebagai ujian untuk pertanyaan ini.
John Dvorak
Tampaknya berfungsi dengan baik sekarang :)
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

Menggunakan algoritma pembungkusan hadiah. Hasilnya dimulai dengan titik paling kiri dan membungkus berlawanan arah jarum jam.

Contoh: h([(1, 1), (2, 2), (3, 3), (1, 3)])kembali[(1, 3), (1, 1), (3, 3)]

kotak kardus
sumber
Apakah Anda tidak memerlukan printuntuk mendapatkan output?
Dr. belisarius
Saya pikir dengan "output" yang Anda maksud adalah output dari fungsi. Apakah Anda ingin fungsi mencetak hasil alih-alih mengembalikannya?
cardboard_box
Saya pikir bahwa persyaratan the output list can be in any reasonable formatsudah cukup jelas. Apakah Anda pikir itu perlu dinyatakan secara eksplisit?
Dr. belisarius
Tampaknya poin output Anda tidak selalu cocok dengan yang input jika floating point digunakan. Misalnya h([(0, 1), (0,1), (0.1 , 1)])memberi saya[(0, 1), (0.10000000000000001, 1)]
Dr. belisarius