Количество отверстий в многоугольнике

11

Проблема : Подсчитайте количество отверстий в соединенном многоугольнике. Связность многоугольника гарантируется условием, что каждый треугольник во входной триангуляции разделяет по крайней мере 1 сторону с другим треугольником и что существует только один такой связанный набор треугольников.

Входной список Lиз nточек на плоскости , а также список Tиз 3-кортежей с элементами из 0...n-1. Для каждого элемента в Tкортеже (t_1,t_2,t_3)представлены три вершины (из списка L) треугольника в триангуляции. Обратите внимание, что это триангуляция в смысле «полигональной триангуляции» , потому что в этом Tперекрытии никогда не будет двух треугольников . Дополнительным условием является то, что вам не придется очищать входные данные Lи Tне содержать повторов.

Пример 1. Если L = {{0,0},{1,0},{0,1},{1,2}}и T = {{0,1,2},{1,2,3}}тогда указанный полигон имеет число отверстий 0.

фигура 1

Пример 2 : если L = {{0,0},{1,0},{2,0},{2,1},{2,2},{1,2},{0,2},{0,1},{.5,.5},{1.5,.5},{1.5,1.5},{.5,1.5}}и T = {{5,6,11},{5,10,11},{4,5,10},{3,8,10},{2,3,9},{2,8,9},{1,2,8},{0,1,8},{0,8,11},{0,7,11},{6,7,11},{3,4,10}}тогда ввод полигона должен привести к выводу 2.

фигура 2

Задача состоит в том, чтобы написать самую короткую программу (или функцию), которая принимает Lи в Tкачестве входных данных и возвращает количество отверстий. «Победитель» будет признан как запись с наименьшим количеством символов (ориентировочная дата окончания 1 июня).

Пример форматирования ввода (обратите внимание на индексирование 0):

0,0
1,0
0,1
1,2
0,1,2
1,2,3    
Kaya
источник
1
«Связность многоугольника гарантируется условием, что каждый треугольник во входной триангуляции разделяет по крайней мере 1 сторону с другим треугольником». - нет Это не достаточное условие. Взять, к примеру T=1,2,3/1,2,4/5,6,7/5,6,8,. Каждый треугольник разделяет ребро с другим треугольником, но триангуляция отключена
Джон Дворжак
Можем ли мы предположить, что входные данные представляют действительную частичную триангуляцию (два треугольника не перекрываются и треугольник не присутствует дважды), и триангуляция связана?
Джон Дворак
Можем ли мы также предположить, что вход связан с ребром в том смысле, что невозможно удалить конечный набор точек, чтобы отсоединить форму? (например: T=1,2,3/1,4,5подключен, но не подключен к границе)
Джон Дворак
2
Я не уверен, почему это дело о сроках начала появляться недавно. Вам разрешено изменить принятый ответ, поэтому нет необходимости устанавливать дату окончания. Разумно иметь представление, что вы подождете неделю, прежде чем выбрать ответ, чтобы не пугать людей, думая, что первый ответ непобедим, но пока вы активны на сайте, вы можете изменить выбранный ответ. если кто-то постит лучше. Соответствующие мета-дискуссии включают meta.codegolf.stackexchange.com/q/542/194 и meta.codegolf.stackexchange.com/q/193/194
Питер Тейлор

Ответы:

5

GolfScript (23 символа)

~.{2*2/~}%{$}%.&,@@+,-)

Предполагает формат ввода, используя обозначение массива GolfScript и кавычки (или целые) координаты. Например

$ golfscript codegolf11738.gs <<<END
[["0" "0"] ["1" "0"] ["2" "0"] ["2" "1"] ["2" "2"] ["1" "2"] ["0" "2"] ["0" "1"] [".5" ".5"] ["1.5" ".5"] ["1.5" "1.5"] [".5" "1.5"]] [[5 6 11] [5 10 11] [4 5 10] [3 8 10] [2 3 9] [2 8 9] [1 2 8] [0 1 8] [0 8 11] [0 7 11] [6 7 11] [3 4 10]]
END
2

( Онлайн эквивалент )

или же

$ golfscript codegolf11738.gs <<<END
[[0 0] [1 0] [0 1] [1 2]] [[0 1 2] [1 2 3]]
END
0

( Онлайн эквивалент )

Питер Тейлор
источник
5

Python, 71

Далее следует программа (а не функция ), которая вычисляет желаемое число.

len(set().union(*(map(frozenset,zip(t,t[1:]+t))for t in T)))-len(L+T)+1

Пример использования:

>>> L = ((0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1),(.5,.5),(1.5,.5),(1.5,1.5),(.5,1.5))
>>> T = ((5,6,11),(5,10,11),(4,5,10),(3,8,10),(2,3,9),(2,8,9),(1,2,8),(0,1,8),(0,8,11),(0,7,11),(6,7,11),(3,4,10))
>>> len(set().union(*(map(frozenset,zip(t,t[1:]+t))for t in T)))-len(L+T)+1
2
Восстановить Монику
источник
+1 за использование сплата, использование frozenset вместо сортировки, zip (не могу сказать, что когда-либо использовал его раньше, нужно познакомиться.)
Kaya
3

APL, 36

{1+(⍴⊃∪/{{⍵[⍋⍵]}¨,/3 2⍴⍵,⍵}¨⍵)-⍴⍺,⍵}

Функция принимает в Lкачестве левого аргумента и Tправого.

Например:

      L←(0 0)(1 0)(0 1)(1 2)
      T←(0 1 2)(1 2 3)
      L{1+(⍴⊃∪/{{⍵[⍋⍵]}¨,/3 2⍴⍵,⍵}¨⍵)-⍴⍺,⍵}T
0
      L←(0 0)(1 0)(2 0)(2 1)(2 2)(1 2)(0 2)(0 1)(.5 .5)(1.5 .5)(1.5 1.5)(.5 1.5)
      T←(5 6 11)(5 10 11)(4 5 10)(3 8 10)(2 3 9)(2 8 9)(1 2 8)(0 1 8)(0 8 11)(0 7 11)(6 7 11)(3 4 10)
      L{1+(⍴⊃∪/{{⍵[⍋⍵]}¨,/3 2⍴⍵,⍵}¨⍵)-⍴⍺,⍵}T
2

Объяснение, идущее справа налево:

  • ⍴⍺,⍵объединяет два входных вектора и возвращает их длину ( V + F)
  • Шаг внутри следующего блока:
    • ¨⍵ применяет функцию слева к каждому элементу правого аргумента и возвращает результат
    • ⍵,⍵ возвращает правильный аргумент, объединенный с самим собой
    • 3 2⍴формирует векторный аргумент в три пары. В этом случае он соединяет вместе первый и второй, третий и первый, а также второй и третий элементы вектора.
    • ,/ объединяет векторный аргумент
    • ⍵[⍋⍵] сортирует правильный аргумент
    • ∪/ отфильтровывает любые дубликаты
    • ⍴⊃ превращает вложенный скаляр в вектор и возвращает его длину.
    • Вся функция возвращает количество ребер в форме ( E)
  • 1 говорит само за себя (я надеюсь ...)

Вся функция затем возвращает 1+E-(V+F), или 1-(F+V-E).

летучесть
источник
Практически точно, что делает мое решение GolfScript. Я удивлен, что это намного дольше, чем GolfScript.
Питер Тейлор
@PeterTaylor Я был удивлен, что ваше решение для GolfScript было намного короче! (Но опять же , это является GolfScript)
Волатильность
2

Mathematica, 93 (пока не очень много в гольфе)

f[l_, t_] :=  Max@MorphologicalComponents[Erosion[Graphics[
                                                        GraphicsComplex[l, Polygon[t + 1]]], 1]] - 1

(Пробелы добавлены для ясности)

Тестирование:

f[{{0, 0}, {1, 0}, {0, 1}, {1, 2}}, {{0, 1, 2}, {1, 2, 3}}]
(*
 -> 0
*)

{l, t} = {{{0, 0}, {1,   0}, {2,    0}, {2,     1}, {2,    2}, {1, 2}, {0, 2}, 
           {0, 1}, {.5, .5}, {1.5, .5}, {1.5, 1.5}, {.5, 1.5}}, 

           {{5, 6, 11}, {5, 10, 11}, {4, 5, 10}, {3, 8, 10}, {2, 3,  9}, 
            {2, 8,  9}, {1,  2,  8}, {0, 1,  8}, {0, 8, 11}, {0, 7, 11}, {6, 7, 11}, {3, 4, 10}}};
f[l, t]
 (*
  -> 2
 *)
Доктор Велизарий
источник
Разве это не зависит от треугольников или отверстий, имеющих определенный минимальный размер (аргумент Erosion)?
Джон Дворжак
@JanDvorak Возможно, я ошибаюсь, но я думаю, что если вы не используете арифметику с бесконечной точностью, любое решение будет работать, пока вы не достигнете определенного минимального размера (вам придется решить, будут ли три точки выровнены или нет). Просто в таком решении проблема прямо сформулирована.
д-р Велизарий
если вы используете топологический подход, вам не нужно. Если есть три коллинеарных точки, то вам нужен треугольник с нулевой площадью - иначе у вас есть отверстие.
Джон Дворжак
@belisarius. Вот ответ, который я получил от технической поддержки Wolfram о несоответствии наших результатов: «Здравствуйте. Спасибо за ваше письмо. Я подтвердил, что ваш код дает разные результаты на Mac и Windows. Я не думаю, что это предполагаемое поведение, поэтому Я подал отчет нашим разработчикам по этой проблеме. Я обязательно передам любую полезную информацию, которую я получу от наших разработчиков по этому вопросу. Пожалуйста, дайте мне знать, если у вас есть какие-либо дополнительные вопросы ... Техническая поддержка Wolfram Research , Inc. "
DavidC
@DavidCarraher "Да, у меня есть дополнительные вопросы: вы отправите мне чек на каждую ошибку?"
Доктор Белизарий
2

Рубин, 239 символов (227 корпус)

def f t
e=t.flat_map{|x|x.permutation(2).to_a}.group_by{|x|x}.select{|_,x|x.one?}.keys
n=Hash[e]
(u,n=n,n.dup;e.map{|x|a,b=*x;n[a]=n[n[a]]=n[b]})until n==u
n.values.uniq.size+e.group_by(&:last).map{|_,x|x.size}.reduce(-1){|x,y|x+y/2-1}
end

обратите внимание, что я рассматриваю только топологию. Я не использую позиции вершин в любом случае.

вызывающая сторона (ожидает T в формате Mathematica или JSON):

input = gets.chomp
input.gsub! "{", "["
input.gsub! "}", "]"
f eval(input)

Тест:

f [[0,1,2],[1,2,3]]
#=> 0
f [[5, 6, 11], [5, 10, 11], [4, 5, 10], [3, 8, 10], [2, 3, 9], [2, 8, 9], [1, 2, 8], [0, 1, 8], [0, 8, 11], [0, 7, 11], [6, 7, 11], [3, 4, 10]]
#=> 2
f [[1,2,3],[3,4,5],[5,6,1],[2,3,4],[4,5,6],[6,1,2]]
#=> 1
Джон дворак
источник
Yay, эйлерова характеристика подхода. Вот как я это сделал на питоне.
Кая
2
@Kaya. (См. Яйцо Колумба en.wikipedia.org/wiki/Egg_of_Columbus ). Как только кто-то дал ответ Эйлера на ваш вопрос, вероятность того, что другие последуют, значительно возрастает. Уверяю вас, гораздо сложнее и приятно открывать этот подход самостоятельно, только после того, как вы подключитесь к работе Эйлера с многогранниками.
DavidC
2

Mathematica 76 73 72 67 62

После долгих экспериментов я понял, что точное расположение вершин не имеет значения, поэтому я представил проблему с графами. Существенные инварианты, количество треугольников, ребер и вершин оставались неизменными (при условии, что пересечение линий было исключено).

На графике было два вида внутренних «треугольников»: это были предположительно лица, то есть «заполненные» треугольники, и те, где их не было. Количество внутренних граней не имело никакого отношения к ребрам или вершинам. Это означало, что прокалывание отверстий в полностью «заполненных» графиках только уменьшало количество граней. Я играл систематически с вариациями среди треугольников, отслеживая грани, вершины и ребра. В конце концов я понял, что число дырок всегда было равно 1 - #faces - # vertices + #edges. Это оказалось равным 1 минус характеристика Эйлера (о которой я знал только в контексте правильных многогранников), хотя длина ребер явно не имела значения.

Приведенная ниже функция возвращает количество отверстий при вводе вершин и треугольников. В отличие от моего более раннего представления, это не полагается на сканирование изображения. Вы можете думать об этом как о 1 - характеристике Эйлера, то есть 1 - (F + V -E) где F= #faces, V= # вершин, E= # ребер. Функция возвращает количество отверстий, 1 - (F + V -E)учитывая фактические грани (треугольники) и вершины.

Легко показать, что удаление любого треугольника снаружи комплекса не влияет на характеристику Эйлера, независимо от того, разделяет ли он одну или две стороны с другими треугольниками.

Примечание: строчная буква vбудет использоваться вместо Lоригинальной формулировки; то есть он содержит сами вершины (не V, количество вершин)

fиспользуется для Tоригинальной рецептуры; то есть содержит треугольники, представленные в виде упорядоченной тройки индексов вершин.

Код

z=Length;1-z@#2-z@#+z[Union@@(Sort/@{#|#2,#2|#3,#3|#}&@@@#2)]&

(Спасибо мистеру Волшебнику за то, что он сбрил 5 символов за счет исключения правила замены.)


Пример 1

v = {{0, 0}, {1, 0}, {0, 1}, {1, 2}}; f = {{0, 1, 2}, {1, 2, 3}};

z=Length;1-z@#2-z@#+z[Union@@(Sort/@{#|#2,#2|#3,#3|#}&@@@#2)]&[v, f]

0

Ноль дыр.


Пример 2

v = {{0, 0}, {1, 0}, {2, 0}, {2, 1}, {2, 2}, {1, 2}, {0, 2}, {0, 1} , {.5, .5}, {1.5, .5}, {1.5, 1.5}, {.5, 1.5}}; f = {{5, 6, 11}, {5, 10, 11}, {4, 5, 10}, {3, 8, 10}, {2, 3, 9}, {2, 8, 9} , {1, 2, 8}, {0, 1, 8}, {0, 8, 11}, {0, 7, 11}, {6, 7, 11}, {3, 4, 10}};

z=Length;1-z@#2-z@#+z[Union@@(Sort/@{#|#2,#2|#3,#3|#}&@@@#2)]&[v, f]

2

Таким образом, 2 отверстия находятся в примере 2.

DavidC
источник
Вы в основном растеризуете триангуляцию и выгружаете графическую библиотеку на это изображение? Разве это не терпит неудачу, если отверстие слишком маленькое?
Джон Дворак
1
Ваш второй пример возвращает 0 здесь (вот почему я не использовал MorphologicalEulerNumber[]). Мма 9.01, Win XP.
Доктор Велизарий
Я также использую 9.0.1, но на Mac. Вы говорите, что Mathematica возвращает другой ответ от моего в Windows? Если это так, это звучит как ошибка (в версии для Windows XP).
DavidC
@DavidCarraher Yep: i.stack.imgur.com/LKcr1.png
Доктор Белизарий
@ Ян Дворак. MorphologicalEulerNumberиногда требуется изображение; он отказывается принимать графический объект. В этих случаях размер отверстия и разрешение имеют решающее значение (см. Codegolf.stackexchange.com/questions/8706/… ). Но здесь он работает напрямую с объектом Graphics, который явно содержит все вершины. Я предполагал (или надеялся), что он будет использовать подход, который не зависит от изображения. Хотел бы я знать, как он пытался решить проблему. Возможно, некоторые ошибки в исходном коде функции прояснят ситуацию.
DavidC
1

Python, 107

Я понял, что брать пары было короче, чем from itertools import*печатать combinations(). Тем не менее, я также заметил, что мое решение основывалось на входных треугольных гранях, чьи вершины перечислены в последовательном порядке. Поэтому выигрыш в количестве персонажей не так велик.

f=lambda l,t:1-len(l+t)+len(set([tuple(sorted(m))for n in[[i[:2],i[1:],[i[0],i[2]]]for i in t]for m in n]))

Питон, 115

Характерный подход Эйлера, многословия itertools кажется невозможным избежать. Интересно, будет ли дешевле использовать более прямую технику для создания пар вершин?

from itertools import*
f=lambda l,t:1-len(l+t)+len(set([m for n in[list(combinations(i,2)) for i in t]for m in n]))

Пример использования:

> f([[0,0],[1,0],[0,1],[1,2]],[[0,1,2],[1,2,3]])
> 0
> f([[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[.5,.5],[1.5,.5],[1.5,1.5],[.5,1.5]],[[5,6,11],[5,10,11],[4,5,10],[3,8,10],[2,3,9],[2,8,9],[1,2,8],[0,1,8],[0,8,11],[0,7,11],[6,7,11],[3,4,10]])
> 2
Kaya
источник