Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Получить точки попавшие в полигон 
V
    Опции темы
W4FhLF
Дата 11.12.2011, 19:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 5
Всего: 121



Привет всем!

Есть сеть точек и некий полигон, граница которого обозначена ломаной линией (см. Example.png), нужно получить те точки, которые попали в полигон. 
Какое решение будет оптимальным?
Количество точек может быть 1000000, при этом полигон может охватывать ~30%




Это сообщение отредактировал(а) W4FhLF - 11.12.2011, 19:14

Присоединённый файл ( Кол-во скачиваний: 26 )
Присоединённый файл  Example.png 46,67 Kb


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
maxdiver
Дата 11.12.2011, 19:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 381
Регистрация: 29.1.2008
Где: Саратов

Репутация: 16
Всего: 18



Вертикальная декомпозиция?

Рассмотрим все иксы, в которых имеются вершины полигона, и отсортируем их: пусть это x[1] ... x[n]. Тогда внутри каждой вертикальной полосы x[i]..x[i+1] нет вершин полигона. Т.е., зафиксировав некоторую полосу x[i]...x[i+1], мы получим, что все стороны полигона либо пересекают всю эту полосу, либо совсем не пересекают (граничные касания в точке x[i] или x[i+1] нас не интересуют). Мы можем построить список таких сторон в порядке их следования снизу вверх, а также построить список точек, попавших в эту полосу x[i]...x[i+1]. Просматривая полосу снизу вверх, у нас будут события двух видов: "встретили сторону" (что значит, что мы зашли внутрь полигона, или, наоборот, вышли из него - если были внутри), и "встретили точку" - тогда ответом для этой точки будет просто текущее значение флага "внутри_полигона".

Асимптотика решения - O(n^2 log n + m log m), где n - количество вершин в полигоне, m - количество точек. Если у вас n тоже большое, то не прокатит. (Впрочем, если полигон не имеет очень сложной конфигурации, то n^2 почти наверняка превратится в что-то порядка n - особенно если повернуть всю картинку на случайный угол.)
PM MAIL WWW ICQ   Вверх
maxdiver
Дата 11.12.2011, 20:07 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 381
Регистрация: 29.1.2008
Где: Саратов

Репутация: 16
Всего: 18



По асимптотике более надёжным выглядит такой вариант: сканировать всю картину слева направо вертикальной прямой: эта прямая движется с x=-INF до x=+INF, и, пока она движется, она встречает события трёх типов:
1) вертикальная прямая начинает пересекать какую-то сторону полигона;
2) вертикальная прямая заканчивает пересекать какую-то сторону полигона;
3) на вертикальную прямую попадает точка-запрос.

Если мы будем поддерживать текущий список пересекаемых нами сторон в порядке их следования по игреку (в виде сбалансированного дерева (например, set<> в C++)), то обрабатывать события типа 3) становится тоже понятно как: мы просто находим сторону, ближайшую к нам снизу, и смотрим, какую чётность имеет эта сторона в списке (нечётная - значит, точка-запрос лежит внутри полигона, чётная - вне).

Это решение уже гарантирует нормальную асимптотику O ((n + m) log n), но аккуратно написать его - не самая простая задача.
PM MAIL WWW ICQ   Вверх
W4FhLF
Дата 11.12.2011, 23:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 5
Всего: 121



maxdiver, последний вариант звучит хорошо.  "Точка-запрос" в вашем описании это что? Любая точка на вертикальной линии, которая принадлежит сети?

PS Количество вершин небольшое (< 100)


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
maxdiver
Дата 12.12.2011, 00:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 381
Регистрация: 29.1.2008
Где: Саратов

Репутация: 16
Всего: 18



Точки-запросы - это те самые 1000000 точек, которые надо проверить на принадлежность полигону.

Во втором алгоритме мы "сканируем" всё вертикальной прямой слева направо, останавливаясь на иксах, равных иксам вершин полигона либо иксам точек-запросов.

Если количество вершин всего 100, то я бы всё же советовал первый вариант - он весьма просто пишется, и достаточно эффективен (самое узкое место в нём - это отсортировать точки по их игреку, а всё остальное работает за линейное время - ну т.е. за 1000000 + 100^2). (Наверное, я в том посте невнятно написал про асимптотику - чтобы понятнее, в вашем случае асимптотика превратится в O (100^2 * log(100) + 1000000 * log (1000000)), что не так и много - примерно как обычная сортировка).

Это сообщение отредактировал(а) maxdiver - 12.12.2011, 00:57
PM MAIL WWW ICQ   Вверх
Silent
Дата 12.12.2011, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 252
Регистрация: 3.10.2006

Репутация: 1
Всего: 9



Я бы реализовал это методом суммирования углов. Сложность как O(n*m) (т.е. 1000000*100), легко пишется, легко параллелится, легко ускоряется (векторизуется).
PM MAIL   Вверх
_Y_
Дата 12.12.2011, 11:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1651
Регистрация: 27.11.2006

Репутация: 8
Всего: 34



Для прикола еще такой метод:

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

Можно еще и сэкономить на проверке что с чем граничит. Для этого при заливке заменяем на желтый не только белый фон, но и сами черные точки. Потом проверяем по координатам точек. Если в данных координатах пиксель пожелтел - значит точка была внутри полигона. Жаль только, что сами точки при этом трагически гибнут smile 

И еще. Если понятия "внутри полигона" и "на линии полигона" различать не надо, заливку надо проводить красным, т.е. цветом самого полигона.



--------------------
Я вот в этом поучаствовал: http://sbor-nik.appspot.com/kick.jsp?id=sbor5737960678883328 (на правах саморекламы:)
PM MAIL WWW   Вверх
W4FhLF
Дата 12.12.2011, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 5
Всего: 121



Цитата(Silent @  12.12.2011,  11:29 Найти цитируемый пост)
Я бы реализовал это методом суммирования углов. Сложность как O(n*m) (т.е. 1000000*100), легко пишется, легко параллелится, легко ускоряется (векторизуется). 


Метод описанный на той же страничке с количеством пересечений ребёр с произвольным лучом имеют такую сложность и легко параллелится. Чем метод суммирования углов лучше?

Но вообще сложность алгоритма предложенного maxdiver'ом всё равно меньше получается. 


_Y_, исходная задача никакого отношения к изображениям не имеет smile 



--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
maxdiver
Дата 12.12.2011, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 381
Регистрация: 29.1.2008
Где: Саратов

Репутация: 16
Всего: 18



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

Интересно, что, кажется, второй алгоритм можно превратить в онлайновый алгоритм (т.е. написать алгоритм таким образом, чтобы он отвечал на запрос для одной указанной точки, за время O (log n)).
В самом деле, второй алгоритм основывается на хранении списка сторон полигона, и поддержании этого списка при движении слева направо. Тупо сохранять все варианты этого списка после каждого изменения - не хочется (потому что получится O (n^2) памяти), но без этого никуда, если мы хотим отвечать на запросы в онлайне.
Выход - персистентные деревья. В самом деле, любая вставка/удаление в красно-чёрном дереве приводит к изменении O(log n) вершин красно-чёрного дерева. И если мы будем не менять значения в вершинах, а создавать новые экземпляры изменяемых вершин, то мы тем самым и старое дерево не изменим, и в то же время получим новое дерево. Таким образом, при каждой вставке/удалении в красно-чёрном дереве будет дополнительно выделяться O (log n) памяти, но зато мы в итоге получим компактное представление всей истории изменения списков.

Итого - мы получили решение с препроцессингом O (n log n) и потреблением памяти O (n log n), и проверкой принадлежности для одной точки за O (log n). Клёвый результат, не знал, что его можно достичь так просто (если, конечно, я не допустил оплошности в логической цепочке).

P.S. При ручной реализации, конечно, стоит использовать не красно-чёрные, а декартовы деревья: они пишутся гораздо проще, а асимптотику имеют ту же.
PM MAIL WWW ICQ   Вверх
_Y_
Дата 12.12.2011, 14:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1651
Регистрация: 27.11.2006

Репутация: 8
Всего: 34



Цитата(W4FhLF @  12.12.2011,  12:33 Найти цитируемый пост)
задача никакого отношения к изображениям не имеет


А я и не настаиваю smile 

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

1. Заранее стриоим растр с залитым полигоном.
2. Каждую вновьприбывшую точку просто проверяем на предмет попадания в поле нужного цвета.

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

Но это весьма частный случай, конечно.


--------------------
Я вот в этом поучаствовал: http://sbor-nik.appspot.com/kick.jsp?id=sbor5737960678883328 (на правах саморекламы:)
PM MAIL WWW   Вверх
W4FhLF
Дата 1.1.2012, 17:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 5
Всего: 121



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


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
Pavia
Дата 1.1.2012, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 6.12.2008

Репутация: 11
Всего: 12



Сетка регулярная значит алгоритм не отличается от алгоритма рисования закрашенного многоугольника.
Можно подсмотреть в любой графической бибилиотеке.

Второй способ.
Задача решается при помощи подвижного контура.
Берётся точка с заведомо известным координатами внутри полигона.
Такую точку легко найти из координат заданного многоугольника.
Вокруг точки строиться подвижный контур. Который будет разрастаться. 
Разрастаться он будет по контуру перемещаясь либо до ближаших точек к соседним либо если при разрастания произошло пересечение с заданным контуром, то помечаем сегмент пренадлежашей этому контуру.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Алгоритмы"

maxim1000

Форум "Алгоритмы" предназначен для обсуждения вопросов, связанных только с алгоритмами и структурами данных, без привязки к конкретному языку программирования и/или программному продукту.


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, maxim1000.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Алгоритмы | Следующая тема »


 




[ Время генерации скрипта: 0.0693 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.