Знайдіть опуклий корпус множини 2D точок


20

Забиваючи набір цвяхів у дерев’яну дошку і обмотуючи навколо них гумку, ви отримуєте опуклий корпус .

введіть тут опис зображення

Ваша місія, якщо ви вирішите її прийняти, - це знайти опуклий корпус заданого набору 2D точок.


Деякі правила:

  • Запишіть це як функцію, аргумент списку точок (у будь-якому форматі)
  • Вихід повинен бути списком точок у опуклому корпусі, вказаному за годинниковою або проти годинникової стрілки, починаючи з будь-якої з них
  • Список вихідних даних може бути у будь-якому розумному форматі, де координати кожної точки чітко відрізняються. (Наприклад, НЕ єдиний тьмяний список {0.1, 1.3, 4, ...})
  • Якщо три або більше точок у відрізку опуклого корпусу вирівняні, на виході слід тримати лише дві крайності

Приклад даних:

Зразок 0

Вхід:

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

Вихід:

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

Графіка математики (Цифри просто показові)

Зразок 1

Вхід:

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

Вихід:

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

Графіка математики

Зразок 2

Вхід:

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

Вихід:

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

Графіка математики

Діють стандартні правила гольф-коду. Немає спеціальних бібліотек геометрії. Коротший код виграє.

Редагуйте 1

Тут ми шукаємо алгоритмічну відповідь, а не випуклий пошук корпусу заздалегідь запрограмований режим, як цей у MatLab або цей у Mathematica

Редагуйте 2

Відповідь на коментарі та додаткову інформацію:

  1. Можна припустити, що список вводу містить мінімальну кількість балів, яка вам підходить. Але ви повинні забезпечити правильну обробку вирівняних (під) наборів.
  2. Ви можете знайти повторні точки у списку вводу
  3. Максимальна кількість балів повинна бути обмежена лише наявною пам'яттю
  4. Повторне "плаваюча точка": Вам потрібно мати можливість обробляти вхідні списки з десятковими координатами, як ті, що наведені у зразках. Ви можете зробити це, використовуючи подання з плаваючою комою

.


2
Я прогнозую, що MATLAB виграє цей.
Пол R

Чи можна припустити, що є щонайменше 3 бали? Чи можна вважати, що точки відрізняються? Чи потрібно нам підтримувати координати з плаваючою комою?
Пітер Тейлор

@PeterTaylor приклад вказує, що остання відповідь є вірною
Джон Дворак

Чи можемо ми перезаписати вхід?
Джон Дворак

Проблема з лікуванням колінеарних точок послідовно полягає в тому, що існують проблеми округлення. Нам слід дозволити робити помилки.
Джон Дворак

Відповіді:


2

Рубі, 168 символів

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}

Цей рубіновий код також використовує алгоритм упаковки подарунків. Функція Cприймає масив точок і повертає опуклий корпус як масив.

Приклад:

>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

Математика 151

все ще працює

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

тестування:

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

введіть тут опис зображення


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

Якщо функція не повинна бути доступною, видаліть її f=для гоління ще двох символів.

Введення / вихід - це єдиний масив точок, причому кожна точка визначається символом x,y властивостями. Вхідний масив модифікується, а також повертається (якщо останній не потрібен, видаліть два останні символи).

Пояснення може бути додано пізніше.

Тестовий набір (не працює в oldIE):

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

запропоноване тестове середовище: http://coffeescript.org/


Я спробував це, {{1, 1}, {2, 2}, {3, 3}, {1, 3}}і він повернувся, [{"x" : 1, "y" : 1, "r" : 0}, {"x" : 1, "y" : 3, "r" : 0}, "x" : 2, "y" : 2, "r" : 0.78..}]але я вважаю, що правильна відповідь є певною перестановкою{{3, 3}, {1, 3}, {1, 1}}
д-р belisarius

Проблема @belisarius з колінеарними точками, коли перший, коли іноді виправлений неправильний корпус,
Джон Дворак

@belisarius, будь ласка, додайте це як тестовий випадок до питання.
Джон Дворак

Начебто зараз працює нормально :)
Доктор belisarius

1

Пітон, 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

Використовує алгоритм упаковки подарунків. Результат починається з крайньої лівої точки і загортається проти годинникової стрілки.

Приклад: h([(1, 1), (2, 2), (3, 3), (1, 3)])повертає[(1, 3), (1, 1), (3, 3)]


Вам не потрібен printаби отримати вихід?
Доктор Белісарій

Я подумав, що під "виходом" ви мали на увазі вихід функції. Ви хочете, щоб функція друкувала результат замість того, щоб повертати його?
cardboard_box

Я вважав, що вимагати the output list can be in any reasonable formatдостатньо ясно. Як ви вважаєте, це потрібно чітко зазначити?
Доктор Белісарій

Здається, ваші вихідні точки не завжди відповідають вхідним, якщо використовується плаваюча точка. Наприклад, h([(0, 1), (0,1), (0.1 , 1)])дає мені[(0, 1), (0.10000000000000001, 1)]
доктор belisarius
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.