Поиск луча, который пересекает многоугольник как можно чаще

Вот интересное упражнение:

Пусть P - простой, но не обязательно выпуклый многоугольник и q - произвольная точка, не обязательно в P.

Создайте эффективный алгоритм, чтобы найти отрезок линии, исходящий из q, который пересекает максимальное количество ребер P.

Другими словами, если вы стоите в точке q, в каком направлении вы должны прицелиться в пушку, то пуля пройдет через наибольшее количество стен?

Пуля через вершину Р получает кредит только для одной стены.

Алгоритм O (n log n) возможен. n - количество вершин или ребер, так как это многоугольник, число ребер примерно равно числу вершин.

Вот моя мысль:

Соедините q со всеми вершинами (скажем, есть N вершин) в P вначале. Будут N строк или N-1 пар линий.

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

Я не думаю, что это решение - O (n log n).

Любые идеи?

Ответ 1

Ну, сначала преобразуйте координаты точек в полярную систему с центром в P.

  • Без ограничения общности можно выбрать направление по часовой стрелке как специальное по отношению к координате угла.
  • Теперь давайте проведем круговую прогулку по всем краям многоугольника и заметим начальную и конечную точки этих ребер, где прогулка ведет нас по часовой стрелке относительно P.
  • Позвольте называть конечную точку такого ребра a 'butt' и отправной точкой a 'head'. Это все должно быть сделано в O (n). Теперь нам придется сортировать эти головы и приклады, поэтому с быстрой сортировкой это может быть O(nlog(n)). Мы сортируем их по координате угла (φ) от наименьшего φ вверх, следя за тем, чтобы в случае равной φ координаты считались меньшими, чем приклады (это важно для соответствия последнему правилу проблемы).
  • Как только мы закончим, мы начнем их с наименьшего числа φ, увеличивая текущую сумму всякий раз, когда мы сталкиваемся с прикладом, и уменьшаемся всякий раз, когда сталкиваемся с головой, замечая глобальный максимум, который будет интервалом по координате φ. Это также должно быть сделано в O (n), поэтому общая сложность O(nlog(n)).

Теперь вы могли бы рассказать мне, почему вы задаете такие вопросы?

Ответ 2

Я использовал алгоритм Бориса Ститницкого и написал свое решение на Java. Но я выбрал направление против часовой стрелки и чтобы проверить, какая точка интервала является начальной точкой, а какая точка - конец интервала, который я использовал в перекрестном продукте. Вы можете найти его здесь: https://github.com/Katkov/algorithms/blob/master/BulletAgainstWalls.java