Предложите и обоснуйте алгоритм проверки, является ли произвольный многоугольник на плоскости ортогональным (все углы 90°), используя координатные методы и методы проверки на целые повороты/дикреты; обсудите погрешности численных реализаций

17 Ноя в 07:10
4 +1
0
Ответы
1
Коротко: под «ортогональным многоугольником» обычно понимают многоугольник с поворотами, кратными 90∘90^\circ90 (см. замечание о строго «все углы 90∘90^\circ90» ниже). Основная координатная идея — проверять перпендикулярность соседних рёбер через скалярное произведение и/или проверять, что каждый вектор ребра получается из предыдущего поворотом на ±90∘\pm90^\circ±90 (дискретные повороты). Далее — алгоритмы, обоснование и рекомендации по погрешностям.
Определение/замечание
- Если требовать, чтобы все внутренние углы строго равнялись 90∘90^\circ90, то из формулы суммы углов 180(n−2)=90n180(n-2)=90n180(n2)=90n следует n=4n=4n=4 — т.е. единственные такие простые многоугольники — прямоугольники. В большинстве практических задач под «ортогональным» подразумевают, что каждый поворот между соседними рёбрами равен ±90∘\pm90^\circ±90 (т.е. рёбра имеют только две взаимно перпендикулярные направления); далее я использую это понятие.
Вход: упорядоченный замкнутый список вершин p1,…,pnp_1,\dots,p_np1 ,,pn (считаем pn+1=p1p_{n+1}=p_1pn+1 =p1 ). Вектора рёбер vi=pi+1−pi\,v_i=p_{i+1}-p_ivi =pi+1 pi .
Алгоритм A — простая координатная проверка (скалярные произведения)
1. Предобработка: удалить повторяющиеся подряд вершины; если после этого какие‑то vi=(0,0)v_i=(0,0)vi =(0,0) — либо удалить, либо считать некорректным вводом.
2. Для каждого i вычислить вектор viv_ivi и длины ∥vi∥\|v_i\|vi .
3. Для каждого i проверить перпендикулярность соседних рёбер:
проверить условие
∣vi⋅vi+1∣≤ε ∥vi∥ ∥vi+1∥ |v_i\cdot v_{i+1}|\le\varepsilon\,\|v_i\|\,\|v_{i+1}\|
vi vi+1 εvi vi+1
где vi⋅vi+1=xixi+1+yiyi+1v_i\cdot v_{i+1}=x_i x_{i+1}+y_i y_{i+1}vi vi+1 =xi xi+1 +yi yi+1 .
4. Дополнительно можно проверить замкнутость
∥∑i=1nvi∥≤εpos \Big\|\sum_{i=1}^n v_i\Big\|\le\varepsilon_{\text{pos}}
i=1n vi εpos
(обычно натурально выполняется, если вход корректный).
5. Если все проверки пройдены — многоугольник ортогонален (в смысле поворотов ±90∘\pm90^\circ±90).
Обоснование: для двух векторов перпендикулярность эквивалентна vi⋅vi+1=0v_i\cdot v_{i+1}=0vi vi+1 =0. Т.к. последовательность перпендикулярностей гарантирует, что направления ребёр получаются поворотом на ±90∘\pm90^\circ±90 от предыдущего, все рёбра лежат в двух взаимно перпендикулярных направлениях.
Алгоритм B — проверка целых поворотов (дискретная проверка поворота на ±90∘\pm90^\circ±90)
1. Как выше найти viv_ivi .
2. Для каждого i можно вычислить ориентированный «поворот‑коэффициент» через скаляр и псевдоскаляр (в 2D псевдоскаляр = детерминант):
ai=vi⋅vi+1,bi=vi×vi+1=xiyi+1−yixi+1. a_i = v_i\cdot v_{i+1},\qquad b_i = v_i\times v_{i+1}=x_i y_{i+1}-y_i x_{i+1}.
ai =vi vi+1 ,bi =vi ×vi+1 =xi yi+1 yi xi+1 .
3. Нормализовать: положим
ri=ai+ibi∥vi∥ ∥vi+1∥. r_i = \frac{a_i + i b_i}{\|v_i\|\,\|v_{i+1}\|}.
ri =vi vi+1 ai +ibi .
В точном случае перпендикулярного поворота ri∈{i,−i}r_i\in\{i,-i\}ri {i,i}. Проверяем дистанцию до этих значений:
min⁡(∣ℜri∣, ∣ ∣ℑri∣−1 ∣)≤ε. \min\big(|\Re r_i|,\,|\;|\Im r_i|-1\;|\big)\le\varepsilon.
min(∣ℜri ,∣ℑri 1)ε.
или проще: требуем ∣ai∣≤ε∥vi∥∥vi+1∥|a_i|\le\varepsilon\|v_i\|\|v_{i+1}\|ai εvi ∥∥vi+1 и ∣bi∣|b_i|bi не чрезмерно мал (чтобы исключить вырожденность).
4. Также можно фиксировать знак поворота по знаку bib_ibi (лeво/право).
Преимущество этого подхода — явная дискретизация поворота; удобно для последующей логики (счёт лево/право, связность).
Специальный случай при целых координатах
- Если вершины имеют целые координаты, то все выражения vi⋅vi+1v_i\cdot v_{i+1}vi vi+1 и vi×vi+1v_i\times v_{i+1}vi ×vi+1 целы; перпендикулярность проверяется точно условием
vi⋅vi+1=0. v_i\cdot v_{i+1}=0.
vi vi+1 =0.
Это устраняет проблемы с округлением — используйте целочисленные 64‑/128‑бит операции или bigint при необходимости.
Обработка вырожденных/нежелательных случаев
- Последовательные коллинеарные рёбра дают внутренний угол 180∘180^\circ180 — их надо либо считать некорректными, либо соединять в одно ребро.
- Если нужно, чтобы все внутренние углы были ровно 90∘90^\circ90 (а не 270∘270^\circ270), дополнительно проверяйте ориентацию внутреннего угла: для простого многоугольника вычислите ориентацию полигона (сумма поворотов) и используйте знак детерминанта, чтобы отличать выпуклые/вогнутые углы.
Погрешности численных реализаций — рекомендации
- Использовать относительную нормированную погрешность. Вместо ∣vi⋅vi+1∣≤δ|v_i\cdot v_{i+1}|\le\deltavi vi+1 δ применять
∣vi⋅vi+1∣≤ε ∥vi∥ ∥vi+1∥. |v_i\cdot v_{i+1}|\le\varepsilon\,\|v_i\|\,\|v_{i+1}\|.
vi vi+1 εvi vi+1 ∥.
Для double обычно берут ε\varepsilonε в диапазоне от 10−1210^{-12}1012 до 10−810^{-8}108 в зависимости от расстояний/масштаба данных; можно выбрать
ε=C⋅epsmach⋅S2, \varepsilon = C\cdot \text{eps}_{\text{mach}}\cdot S^2,
ε=Cepsmach S2,
где S=max⁡i∥vi∥S=\max_i\|v_i\|S=maxi vi и CCC — небольшая константа (например 10210^2102).
- Лучше нормализовать: проверять ∣cos⁡θ∣≤ε|\cos\theta|\le\varepsiloncosθε с cos⁡θ=vi⋅vi+1∥vi∥ ∥vi+1∥\cos\theta=\dfrac{v_i\cdot v_{i+1}}{\|v_i\|\,\|v_{i+1}\|}cosθ=vi vi+1 vi vi+1 .
- Если вход результат цифровой обработки (сканирование, округление), целесообразно сначала «привести к сетке»: поворачивать/сдвигать/округлять углы к ближайшим кратным 90∘90^\circ90 при условии, что отклонение мало.
- Для точного результата при рациональных/целых координатах — использовать точную арифметику (целочисленные или рациональные, bigint), или использовать одностороннюю проверку через модульные/целочисленные операции.
- При работе с очень «плохими» данными (малые длины рёбер, близкие к 0) удалить или объединить мелкие ребра, т.к. относительная погрешность там растёт.
Сложность
- О(n) по времени и памяти: один проход по рёбрам вычисляет нужные скалярные и псевдоскалярные произведения.
Краткая сводка (практическая реализация)
- Удалить дубликаты и нулевые вектора.
- Для i=1..n вычислить viv_ivi , проверить ∥vi∥>0\|v_i\|>0vi >0.
- Проверить для всех i условие перпендикулярности
∣vi⋅vi+1∣≤ε ∥vi∥ ∥vi+1∥. |v_i\cdot v_{i+1}|\le\varepsilon\,\|v_i\|\,\|v_{i+1}\|.
vi vi+1 εvi vi+1 ∥.
- При целых входных координатах — проверка точная: vi⋅vi+1=0v_i\cdot v_{i+1}=0vi vi+1 =0.
- При обнаружении нарушения — не ортогонален; иначе — ортогонален (в смысле поворотов ±90∘\pm90^\circ±90).
Если нужно, могу прислать компактный пример кода (C++/Python) с указаниями выбора ε\varepsilonε.
17 Ноя в 08:19
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир