Предикат "левый поворот" — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (Bounding box)
м (rollbackEdits.php mass rollback)
 
(не показано 19 промежуточных версий 8 участников)
Строка 1: Строка 1:
Даны два отрезка AB и CD (они могут вырождаться в точки). Требуется проверить, пересекаются они на плоскости или нет. Для упрощения определения этого факта в вычислительной геометрии используется предикат "левый поворот" (или "по часовой стрелке").
+
Даны два отрезка, которые задаются начальной и конечной точками <tex>a,b\ \mathcal{2}\ \mathbb R^2</tex> и определяются как множества точек <tex>s\ =\ \{(1-t)a + tb,\ t\ \in [0;1]\}</tex>. Требуется проверить существование множества их общих точек. Для определения этого факта в вычислительной геометрии используется предикат ''левый поворот'' (или ''по часовой стрелке'').
 
Рассмотрим возможные расположения точек и самих отрезков относительно друг друга:
 
Рассмотрим возможные расположения точек и самих отрезков относительно друг друга:
  
Строка 6: Строка 6:
 
[[Файл:Touch.jpg]]
 
[[Файл:Touch.jpg]]
  
Одно из решений - определить, лежат ли точки концов отрезков по разные стороны от другого отрезка.
+
Определим, лежат ли точки концов отрезков по разные стороны от другого отрезка.
 
{{Определение
 
{{Определение
 
|definition =
 
|definition =
 
<tex dpi = "120">
 
<tex dpi = "120">
 
$$
 
$$
Left\_Turn(a, b, c) =\left\{
+
\operatorname{LeftTurn}(a, b, c) =\left\{
 
\begin{array}{rl}
 
\begin{array}{rl}
-1 &\mbox{, for}\ (b - a)\times(c - a) < 0\\
+
-1 &\mbox{, if}\ (c - a)\times(b - a) < 0\\
0 &\mbox{, for}\ (b - a)\times(c - a) = 0\\
+
0 &\mbox{, if}\ (c - a)\times(b - a) = 0\\
1 &\mbox{, for}\ (b - a)\times(c - a) > 0
+
1 &\mbox{, if}\ (c - a)\times(b - a) > 0
 
\end{array}
 
\end{array}
 
\right.
 
\right.
Строка 22: Строка 22:
 
}}
 
}}
 
Распишем подробнее:
 
Распишем подробнее:
<tex>(b - a)\times(c - a) = (b_x - a_x)\cdot(c_y - a_y) - (b_y - a_y)\cdot(c_x - a_x) = A - B</tex>
+
<tex dpi = 120>(c - a)\times(b - a) = (c_x - a_x)(b_y - a_y) - (c_y - a_y)(b_x - a_x) = V</tex>
  
 
Какие при этом у нас будут погрешности?  
 
Какие при этом у нас будут погрешности?  
 
Допустим, что все числа положительные и будем писать без модулей:
 
Допустим, что все числа положительные и будем писать без модулей:
  
NB: при сложении складываются абс. погрешности,при умножении складываются отн. погрешности.
+
'''Замечание:''' при сложении складываются абсолютные погрешности, при умножении складываются относительные погрешности.
  
<tex dpi = "150"> \delta (b - a)\times(c - a) = A \cdot \epsilon \cdot (\frac{(b_x + a_x)}{(b_x \cdot a_x)} + \frac{(c_y + a_y)}{(c_y \cdot a_y)}) + B \cdot \epsilon \cdot (\frac{(b_y + a_y)}{(b_y \cdot a_y)} + \frac{(c_x + a_x)}{(c_x \cdot a_x)})</tex>
+
<tex dpi = "150"> \delta (c - a)\times(b - a) = A \varepsilon \left(\frac{(c_x + a_x)}{(c_x \cdot a_x)} + \frac{(b_y + a_y)}{(b_y \cdot a_y)}\right) + B \varepsilon \left(\frac{(c_y + a_y)}{(c_y \cdot a_y)} + \frac{(b_x + a_x)}{(b_x \cdot a_x)}\right)</tex>
  
Заметим, что все координаты (а значит и наши вычисления) производятся в вещественных числах, а это значит, что при вычислениях мы можем допустить ошибку. Точно определить знак нашего выражения поможет вычисление с [[Интервальная арифметика |"интервальной арифметикой"]]. Все исходные переменные будут вырожденными интервалами. Из-за погрешностей, возникающих при округлении вещественных чисел, истинные значения операций нам будут  неизвестны, но они обязательно будет содержаться в посчитанных интервалах.
+
Именно поэтому, когда угол между отрезками АВ и АС ''крайне мал'', мы можем получить неверное значение предиката.
  
TODO: тут еще чего-то написать...
+
[[Файл:Tiny_angle.jpg]]
 +
 
 +
Заметим, что все координаты (а, значит, и наши вычисления) производятся в вещественных числах, а это значит, что при вычислениях мы можем допустить ошибку. Распишем вещественное исчисление:
 +
 
 +
<tex dpi = 140>V = (c - a)\times(b - a) \approx (c_x \ominus a_x)\otimes(b_y \ominus a_y) \ominus (c_y \ominus a_y)\otimes(b_x \ominus a_x) =</tex>
 +
 
 +
<tex dpi = 130>= \big((c_x - a_x)(b_y - a_y)(1 + \delta_1)(1 + \delta_2)(1 + \delta_3)\ -</tex>
 +
 
 +
<tex dpi = 130>-\ (c_y - a_y)(b_x - a_x)(1 + \delta_4)(1 + \delta_5)(1 + \delta_6)\big)(1 + \delta_7) = \tilde{V}</tex>
 +
 
 +
<tex dpi = 130>\mid\delta_i\mid \le \varepsilon_m = 2^{-54}</tex>;
  
Посмотрим внимательнее на наш предикат. Ошибка раскрывается тогда, когда угол между отрезками АВ и АС крайне мал.
+
Получим некую окрестность <tex dpi = 130>|V - \tilde{V}| \le 8 \varepsilon_m</tex>, если ноль попадает в наш интервал, то приходится пользоваться более тяжелой артиллерией, такими как [[Adaptive precision arithmetic|''adaptive precision arithmetic'']], либо [[Интервальная арифметика |''интервальная арифметика'']]. Во второй, исходные переменные будут вырожденными интервалами. Из-за погрешностей, возникающих при округлении вещественных чисел, истинные значения операций нам будут неизвестны, но они обязательно будет содержаться в посчитанных интервалах.
  
[[Файл:Tiny_angle.jpg]]
+
'''Замечание:''' расписанное неравенство смотрите в [[Представление_чисел_с_плавающей_точкой#.D0.A0.D0.B0.D1.81.D1.87.D0.B5.D1.82|''другом конспекте'']]
  
 
=Bounding box=
 
=Bounding box=
Ещё следует обратить внимание на граничные случаи, когда какие-то точки попадают на саму прямую. При этом возникает единственный особый случай, когда вышеописанные проверки ничего не дадут — случай, когда оба отрезка лежат на одной прямой. Этот случай надо рассмотреть отдельно. Для этого достаточно проверить, что проекции этих двух отрезков на оси X и Y пересекаются (часто эту проверку называют "проверкой на bounding box").
+
Ещё следует обратить внимание на граничные случаи, когда какие-то точки попадают на саму прямую. При этом возникает единственный особый случай, когда вышеописанные проверки ничего не дадут — случай, когда оба отрезка лежат на одной прямой. Этот случай рассматривается отдельно. Для этого достаточно проверить, что проекции этих двух отрезков на оси X и Y пересекаются (часто эту проверку называют ''проверкой на bounding box''). Но отметим, что чаще всего данный предикат используют для трех точек, где одна из них относится сразу к двум отрезкам.
  
 
[[Файл:Bounting_box().png]]
 
[[Файл:Bounting_box().png]]
 
[[Файл:Bounting_box_.png]]
 
[[Файл:Bounting_box_.png]]
  
Псевдокод:
+
[[Категория: Вычислительная геометрия]]
 
 
boolean Bounding_Box(точка A, точка B, точка C)
 
if (((A.xx >= C.xx && C.xx >= B.xx) || (A.xx <= C.xx && C.xx <= B.xx))
 
  && ((A.yy >= C.yy && C.yy >= B.yy) || (A.yy <= C.yy && C.yy <= B.yy)))
 
вернуть true
 
вернуть false
 
 
 
или
 
 
 
boolean Bounding_Box(точка A, точка B, точка C, точка D)
 
if (((A.xx > C.xx && A.xx > D.xx && B.xx > C.xx && B.xx > D.xx) || (A.xx < C.xx && A.xx < D.xx && B.xx < C.xx && B.xx < D.xx))
 
  || ((A.yy > C.yy && A.yy > D.yy && B.yy > C.yy && B.yy > D.yy) || (A.yy < C.yy && A.yy < D.yy && B.yy < C.yy && B.yy < D.yy)))
 
return false;
 
return true;
 

Текущая версия на 19:12, 4 сентября 2022

Даны два отрезка, которые задаются начальной и конечной точками [math]a,b\ \mathcal{2}\ \mathbb R^2[/math] и определяются как множества точек [math]s\ =\ \{(1-t)a + tb,\ t\ \in [0;1]\}[/math]. Требуется проверить существование множества их общих точек. Для определения этого факта в вычислительной геометрии используется предикат левый поворот (или по часовой стрелке). Рассмотрим возможные расположения точек и самих отрезков относительно друг друга:

Cross.png Two segments.png Touch.jpg

Определим, лежат ли точки концов отрезков по разные стороны от другого отрезка.

Определение:
[math] $$ \operatorname{LeftTurn}(a, b, c) =\left\{ \begin{array}{rl} -1 &\mbox{, if}\ (c - a)\times(b - a) \lt 0\\ 0 &\mbox{, if}\ (c - a)\times(b - a) = 0\\ 1 &\mbox{, if}\ (c - a)\times(b - a) \gt 0 \end{array} \right. $$ [/math]

Распишем подробнее: [math](c - a)\times(b - a) = (c_x - a_x)(b_y - a_y) - (c_y - a_y)(b_x - a_x) = V[/math]

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

Замечание: при сложении складываются абсолютные погрешности, при умножении складываются относительные погрешности.

[math] \delta (c - a)\times(b - a) = A \varepsilon \left(\frac{(c_x + a_x)}{(c_x \cdot a_x)} + \frac{(b_y + a_y)}{(b_y \cdot a_y)}\right) + B \varepsilon \left(\frac{(c_y + a_y)}{(c_y \cdot a_y)} + \frac{(b_x + a_x)}{(b_x \cdot a_x)}\right)[/math]

Именно поэтому, когда угол между отрезками АВ и АС крайне мал, мы можем получить неверное значение предиката.

Tiny angle.jpg

Заметим, что все координаты (а, значит, и наши вычисления) производятся в вещественных числах, а это значит, что при вычислениях мы можем допустить ошибку. Распишем вещественное исчисление:

[math]V = (c - a)\times(b - a) \approx (c_x \ominus a_x)\otimes(b_y \ominus a_y) \ominus (c_y \ominus a_y)\otimes(b_x \ominus a_x) =[/math]

[math]= \big((c_x - a_x)(b_y - a_y)(1 + \delta_1)(1 + \delta_2)(1 + \delta_3)\ -[/math]

[math]-\ (c_y - a_y)(b_x - a_x)(1 + \delta_4)(1 + \delta_5)(1 + \delta_6)\big)(1 + \delta_7) = \tilde{V}[/math]

[math]\mid\delta_i\mid \le \varepsilon_m = 2^{-54}[/math];

Получим некую окрестность [math]|V - \tilde{V}| \le 8 \varepsilon_m[/math], если ноль попадает в наш интервал, то приходится пользоваться более тяжелой артиллерией, такими как adaptive precision arithmetic, либо интервальная арифметика. Во второй, исходные переменные будут вырожденными интервалами. Из-за погрешностей, возникающих при округлении вещественных чисел, истинные значения операций нам будут неизвестны, но они обязательно будет содержаться в посчитанных интервалах.

Замечание: расписанное неравенство смотрите в другом конспекте

Bounding box

Ещё следует обратить внимание на граничные случаи, когда какие-то точки попадают на саму прямую. При этом возникает единственный особый случай, когда вышеописанные проверки ничего не дадут — случай, когда оба отрезка лежат на одной прямой. Этот случай рассматривается отдельно. Для этого достаточно проверить, что проекции этих двух отрезков на оси X и Y пересекаются (часто эту проверку называют проверкой на bounding box). Но отметим, что чаще всего данный предикат используют для трех точек, где одна из них относится сразу к двум отрезкам.

Bounting box().png Bounting box .png