Squarefinder - поиск правильных четырехугольников


27

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

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

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

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

Ваша цель - найти количество таких областей, которые являются идеальными квадратами. В приведенном выше примере их три:

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

Обратите внимание, что большой квадрат в середине не считается, поскольку он не является отдельной областью, а вместо этого состоит из нескольких меньших непересекающихся областей.

вход

Вы можете написать функцию или полную программу для этой задачи.

Входные данные будут 4nнеотрицательными целыми числами, определяющими nпрямоугольники на плоскости. Каждый прямоугольник представлен двумя противоположными вершинами, например, 4 9 7 8представляет прямоугольник с противоположными вершинами (4, 9)и (7, 8). Обратите внимание, что этот прямоугольник также может быть представлен как 7 8 4 9или 4 8 7 9.

Точный формат ввода является гибким (например, строка, разделенная пробелами, строка, разделенная запятыми, один массив целых чисел, список кортежей координат и т. Д.), Но, пожалуйста, будьте разумны и приведите пример того, как запустить свой код в своем посте. Вы не можете изменить порядок ввода.

Для простоты можно предположить, что никакие два ребра не будут перекрываться - это включает перекрытие в вершине. В частности, это означает, что никакие два прямоугольника не будут касаться от края до края или от угла к углу, и прямоугольники будут иметь ненулевую площадь.

Выход

Ваша программа должна напечатать или вернуть одно целое число, которое является числом квадратных областей.

счет

Это код гольф, поэтому код в наименьшем количестве байтов выигрывает.


Контрольные примеры

Входные данные:

0 0 5 5
6 8 10 4
14 16 11 13
19 1 18 2

Выход:

4

Это просто четыре непересекающихся квадрата:

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


Входные данные:

2 1 3 11
1 10 5 19
6 10 11 3
8 8 15 15
13 13 9 5
15 1 19 7
17 19 19 17

Выход:

3

Это пример теста в начале поста.


Входные данные:

0 9 15 12
6 3 18 15
9 6 12 20
13 4 17 8

Выход:

7

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


Входные данные:

5 9 11 10
5 12 11 13
6 8 7 14
9 8 10 14
13 8 14 9
13 10 14 14

Выход:

14

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


Входные данные:

0 99999 100000 0

Выход:

0

Это всего лишь один большой прямоугольник.


Входные данные:

0 99999 100000 0
2 1 142857 285714

Выход:

1

Два больших прямоугольника, которые перекрываются.

Ответы:


9

SQL (POSTGIS), 286 269 261 240 226 218 216

Это запрос на расширение PostGIS для PostgreSQL. Я не посчитал входные значения в общем.

SELECT SUM(1)FROM(SELECT(ST_Dump(ST_Polygonize(g))).geom d FROM(SELECT ST_Union(ST_Boundary(ST_MakeEnvelope(a,b,c,d)))g FROM(VALUES
-- Coordinate input
(2, 1, 3, 11)
,(1, 10, 5, 19)
,(6, 10, 11, 3)
,(8, 8, 15, 15)
,(13, 13, 9, 5)
,(15, 1, 19, 7)
,(17, 19, 19, 17)
)i(a,b,c,d))i)a WHERE(ST_XMax(d)-ST_XMin(d))^2+(ST_YMax(d)-ST_YMin(d))^2=ST_Area(d)*2

объяснение

Запрос строит геометрию для каждой пары координат. Объединяет внешние кольца, чтобы правильно расположить линии. Превращает результаты в полигоны и проверяет ширину в зависимости от высоты, а площадь удваивается относительно суммы квадратов каждой стороны.

Он будет работать как отдельный запрос к любой базе данных PostgreSQL с расширением PostGIS.

Править Нашел еще пару.


1
... и Haskell
Оптимизатор

@optimizer Я сомневаюсь, что это продлится :)
MickyT

@MickyT Это превратилось в здоровую конкуренцию. :)
Zgarb

У @zgarb есть немного :-), но я не думаю, что у меня есть что-нибудь еще, чтобы пойти.
MickyT

13

Python 2, 480 436 386 352 байта

exec u"""s=sorted;H=[];V=[]
FRIinput():
 S=2*map(s,zip(*R))
 FiI0,1,2,3:
    c=S[i][i/2];a,b=S[~i]
    FeIs(H):
     C,(A,B)=e
     if a<C<b&A<c<B:e[:]=C,(A,c);H+=[C,(c,B)],;V+=[c,(a,C)],;a=C
    V+=[c,(a,b)],;H,V=V,H
print sum(a==A==(d,D)&c==C==(b,B)&B-b==D-d&1-any(d<X[0]<D&b<y<B Fy,XIH)Fb,aIH FB,AIH Fd,cIV FD,CIV)""".translate({70:u"for ",73:u" in ",38:u" and "})

Принимает список пар координат через STDIN в формате:

[  [(x, y), (x, y)],  [(x, y), (x, y)],  ...  ]

и печатает результат в STDOUT.


Фактическая программа после замены строки:

s=sorted;H=[];V=[]
for R in input():
 S=2*map(s,zip(*R))
 for i in 0,1,2,3:
    c=S[i][i/2];a,b=S[~i]
    for e in s(H):
     C,(A,B)=e
     if a<C<b and A<c<B:e[:]=C,(A,c);H+=[C,(c,B)],;V+=[c,(a,C)],;a=C
    V+=[c,(a,b)],;H,V=V,H
print sum(a==A==(d,D) and c==C==(b,B) and B-b==D-d and 1-any(d<X[0]<D and b<y<B for y,X in H)for b,a in H for B,A in H for d,c in V for D,C in V)

объяснение

Вместо того, чтобы возиться со сложными полигонами, эта программа работает с простыми отрезками. Для каждого входного прямоугольника мы добавляем каждое из четырех ребер в список коллективных сегментов по отдельности. Добавление сегмента в список происходит следующим образом: мы тестируем каждый из существующих сегментов на предмет пересечения с новым сегментом; если мы находим пересечение, мы разделяем оба отрезка в точке пересечения и продолжаем. Чтобы упростить задачу, мы фактически ведем два отдельных списка сегментов: горизонтальный и вертикальный. Поскольку сегменты не перекрываются, горизонтальные сегменты могут пересекать только вертикальные сегменты и наоборот. Более того, это означает, что все пересечения (не считая ребер одного и того же прямоугольника) являются «правильными», т. Е. У нас нет Т-образных пересечений, поэтому «обе стороны» каждого сегмента действительно разделены.

Как только мы создали список (ы) сегментов, мы начинаем считать квадраты. Для каждой комбинации из четырех сегментов (в частности, двух горизонтальных и двух вертикальных сегментов) мы проверяем, образуют ли они квадрат. Кроме того, мы проверяем, что в этом квадрате нет вершины (что может произойти, если, например, у нас есть маленький квадрат внутри большего). Это дает нам желаемое количество. Обратите внимание, что, хотя программа проверяет каждую комбинацию четыре раза в разных порядках, конкретное упорядочение координат сегмента гарантирует, что мы будем считать каждый квадрат только один раз.


1
Я очень впечатлен тем, как быстро вы решили это и как вы подошли к проблеме! Циклы for заставляют меня идти «конечно, что-то можно сделать ...»
Sp3000

@ Sp3000 Да. Я попытался использовать itertoolsдля петель, но в итоге оказалось больше. Я могу побрить несколько байтов с помощью exec+ замены строк, но ничего особенного.
Ell

4

Haskell, 276 266 250 237 225 222 217 байт

Это становится все короче ... и более запутанным.

(x#i)l=mapM id[[min x i..max x i-1],l]
(y!j)l f=and[p l==p(f[y,j])|p<-map elem$f[y..j]]
s[h,v]=sum[1|[x,j]<-h,[y,i]<-v,x<i,i-x==j-y,(y!j)h$x#i,(x!i)v$y#j]
n=s.foldr(\(x,y,i,j)->zipWith(++)[x#i$[y,j],y#j$[x,i]])[[],[]]

Оцените n [(0,0,5,5),(6,8,10,4),(14,16,11,13),(19,1,18,2)]для первого контрольного примера. Я думаю, что я приближаюсь к пределам игры в гольф этого алгоритма на Haskell.

Эта функция настолько медленная (по крайней мере, O (n 3 ), где n - общий периметр всех прямоугольников на входе), что я не могу оценить ее в двух последних тестовых случаях. Когда я скомпилировал его с включенными оптимизациями и запустил на 400-кратной сокращенной версии [(0,249,250,0),(2,1,357,714)]последнего теста, он закончился чуть более чем за 12 секунд. Исходя из этого, реальный контрольный пример завершится примерно через 25 лет.

Объяснение (частичное, я расширю это, когда у меня будет время)

Сначала мы строим два списка hи vследующим образом. Для каждого прямоугольника на входе мы разбиваем его границу на сегменты длиной 1. Западные конечные точки горизонтальных сегментов сохраняются h, а южные конечные точки вертикальных сегментов v- как списки [x,y]длины 2. Координаты в vсохраняются в обратном порядке. Форма как [y,x]по соображениям игры в гольф. Затем мы просто циклически перебираем оба списка и ищем горизонтальный край [x,j]и вертикальный край [i,y], чтобы x < iи i-x == j-y(таким образом, они были северо-западным и юго-восточным углами квадрата), и проверяли, что границы квадрата находятся в правильных списках hи v, в то время как внутренняя часть координаты нет. Количество положительных экземпляров поиска является выходным.


Молодцы, думаю, мне придется уступить сейчас :)
MickyT

@MickyT Прошла неделя, поэтому я сейчас принял ответ Згарба, но если вам удастся побить его позже, флажок может сдвинуться! Честно говоря, я очень впечатлен тем, как далеко вы двое смогли пройти
Sp3000

@Zgarb заслуженная победа :-)
MickyT

@ Sp3000 спасибо за хороший маленький вызов.
MickyT

@ Sp3000 Спасибо! Мне было очень весело в гольф.
Згарб
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.