Выпуклый многоугольник задан координатами вершин; предложите метод определения, является ли он ортогональным (все углы прямые) с использованием только скалярных произведений, и оцените числовую устойчивость метода
Метод (только скалярные произведения) 1. Пусть вершины идут в порядке обхода p1,…,pnp_1,\dots,p_np1,…,pn. Для каждой стороны вычислить вектор ребра vi=pi+1−pi,i=1,…,n,pn+1≡p1.
v_i=p_{i+1}-p_i,\qquad i=1,\dots,n,\quad p_{n+1}\equiv p_1. vi=pi+1−pi,i=1,…,n,pn+1≡p1. 2. На каждом верху проверить перпендикулярность соседних рёбер через скалярное произведение: di=vi−1⋅vi.
d_i = v_{i-1}\cdot v_i. di=vi−1⋅vi.
Если di=0d_i=0di=0 для всех iii, то все углы прямые. Практическая реализация с учётом масштаба (рекомендуемая): проверять нормализованный косинус угла ci=di∥vi−1∥ ∥vi∥,
c_i=\frac{d_i}{\|v_{i-1}\|\;\|v_i\|}, ci=∥vi−1∥∥vi∥di,
и требовать ∣ci∣≤tol
|c_i|\le\text{tol} ∣ci∣≤tol
или эквивалентно ∣di∣≤tol ∥vi−1∥∥vi∥.
|d_i|\le\text{tol}\,\|v_{i-1}\|\|v_i\|. ∣di∣≤tol∥vi−1∥∥vi∥. Выбор tol: - Для целых координат можно проверить точно di=0d_i=0di=0 (без ошибок округления). - Для вычислений в двойной точности рекомендую взять tol=C⋅u\text{tol}=C\cdot utol=C⋅u с машинным эпсилон u≈2.22⋅10−16u\approx 2.22\cdot10^{-16}u≈2.22⋅10−16 и константой CCC порядка 101010–10410^4104 в зависимости от надёжности данных. Практически часто используют tol∈[10−12,10−10]\text{tol}\in[10^{-12},10^{-10}]tol∈[10−12,10−10]. Числовая устойчивость — оценка и замечания - Скаларное произведение в двумерном случае вычисляется устойчиво (несколько операций), погрешность абсолютного значения did_idi ограничена по порядку величины O(u) ∥vi−1∥∥vi∥O(u)\,\|v_{i-1}\|\|v_i\|O(u)∥vi−1∥∥vi∥. Поэтому нормализация на ∥vi−1∥∥vi∥\|v_{i-1}\|\|v_i\|∥vi−1∥∥vi∥ даёт естественную шкалу ошибки. - Главная проблема — вычитание/погрешности при очень маленьких длинах рёбер: если ∥vi∥\|v_i\|∥vi∥ малы (вершины почти совпадают), проверка теряет смысл — следует заранее отбраковывать или нормировать такие случаи (требовать ∥vi∥>\|v_i\|>∥vi∥> порог). - Если угол близок, но не равен 90∘90^\circ90∘, то нельзя однозначно решить без априорной информации: погрешность порядка ∼u\sim u∼u в нормированном косинусе даёт погрешность в угле порядка uuu радиан; для практических данных используйте более «мягкий» tol. - Сложность алгоритма O(n)O(n)O(n). Краткая рекомендация: вычисляйте ci=vi−1⋅vi∥vi−1∥∥vi∥c_i=\dfrac{v_{i-1}\cdot v_i}{\|v_{i-1}\|\|v_i\|}ci=∥vi−1∥∥vi∥vi−1⋅vi и требуйте ∣ci∣≤10−12|c_i|\le 10^{-12}∣ci∣≤10−12 (или точное равенство для целых координат); перед этим проверьте ненулевые длины рёбер.
1. Пусть вершины идут в порядке обхода p1,…,pnp_1,\dots,p_np1 ,…,pn . Для каждой стороны вычислить вектор ребра
vi=pi+1−pi,i=1,…,n,pn+1≡p1. v_i=p_{i+1}-p_i,\qquad i=1,\dots,n,\quad p_{n+1}\equiv p_1.
vi =pi+1 −pi ,i=1,…,n,pn+1 ≡p1 .
2. На каждом верху проверить перпендикулярность соседних рёбер через скалярное произведение:
di=vi−1⋅vi. d_i = v_{i-1}\cdot v_i.
di =vi−1 ⋅vi . Если di=0d_i=0di =0 для всех iii, то все углы прямые.
Практическая реализация с учётом масштаба (рекомендуемая):
проверять нормализованный косинус угла
ci=di∥vi−1∥ ∥vi∥, c_i=\frac{d_i}{\|v_{i-1}\|\;\|v_i\|},
ci =∥vi−1 ∥∥vi ∥di , и требовать
∣ci∣≤tol |c_i|\le\text{tol}
∣ci ∣≤tol или эквивалентно
∣di∣≤tol ∥vi−1∥∥vi∥. |d_i|\le\text{tol}\,\|v_{i-1}\|\|v_i\|.
∣di ∣≤tol∥vi−1 ∥∥vi ∥.
Выбор tol:
- Для целых координат можно проверить точно di=0d_i=0di =0 (без ошибок округления).
- Для вычислений в двойной точности рекомендую взять tol=C⋅u\text{tol}=C\cdot utol=C⋅u с машинным эпсилон u≈2.22⋅10−16u\approx 2.22\cdot10^{-16}u≈2.22⋅10−16 и константой CCC порядка 101010–10410^4104 в зависимости от надёжности данных. Практически часто используют tol∈[10−12,10−10]\text{tol}\in[10^{-12},10^{-10}]tol∈[10−12,10−10].
Числовая устойчивость — оценка и замечания
- Скаларное произведение в двумерном случае вычисляется устойчиво (несколько операций), погрешность абсолютного значения did_idi ограничена по порядку величины O(u) ∥vi−1∥∥vi∥O(u)\,\|v_{i-1}\|\|v_i\|O(u)∥vi−1 ∥∥vi ∥. Поэтому нормализация на ∥vi−1∥∥vi∥\|v_{i-1}\|\|v_i\|∥vi−1 ∥∥vi ∥ даёт естественную шкалу ошибки.
- Главная проблема — вычитание/погрешности при очень маленьких длинах рёбер: если ∥vi∥\|v_i\|∥vi ∥ малы (вершины почти совпадают), проверка теряет смысл — следует заранее отбраковывать или нормировать такие случаи (требовать ∥vi∥>\|v_i\|>∥vi ∥> порог).
- Если угол близок, но не равен 90∘90^\circ90∘, то нельзя однозначно решить без априорной информации: погрешность порядка ∼u\sim u∼u в нормированном косинусе даёт погрешность в угле порядка uuu радиан; для практических данных используйте более «мягкий» tol.
- Сложность алгоритма O(n)O(n)O(n).
Краткая рекомендация: вычисляйте ci=vi−1⋅vi∥vi−1∥∥vi∥c_i=\dfrac{v_{i-1}\cdot v_i}{\|v_{i-1}\|\|v_i\|}ci =∥vi−1 ∥∥vi ∥vi−1 ⋅vi и требуйте ∣ci∣≤10−12|c_i|\le 10^{-12}∣ci ∣≤10−12 (или точное равенство для целых координат); перед этим проверьте ненулевые длины рёбер.