Алгоритм Чана

Алгоритм нахождения выпуклой оболочки множества точек на плоскости
2D-демонстрация алгоритма Чана. Однако следует отметить, что алгоритм делит точки произвольно, а не по x-координате.

В вычислительной геометрии алгоритм Чана [1] , названный в честь Тимоти М. Чана , является оптимальным чувствительным к выходу алгоритмом для вычисления выпуклой оболочки набора точек в 2- или 3-мерном пространстве. Алгоритм занимает время, где — число вершин выходных данных (выпуклой оболочки). В плоском случае алгоритм объединяет алгоритм ( например, сканирование Грэма ) с маршем Джарвиса ( ), чтобы получить оптимальное время. Алгоритм Чана примечателен тем, что он намного проще алгоритма Киркпатрика–Зейделя , и он естественным образом распространяется на 3-мерное пространство. Эта парадигма [2] была независимо разработана Фрэнком Нильсеном в его докторской диссертации. [3] П {\displaystyle P} н {\displaystyle n} О ( н бревно час ) {\displaystyle O(n\log h)} час {\displaystyle ч} О ( н бревно н ) {\displaystyle O(n\log n)} О ( н час ) {\displaystyle O(нч)} О ( н бревно час ) {\displaystyle O(n\log h)}

Алгоритм

Обзор

Для одного прохода алгоритма требуется параметр , который находится между 0 и (количеством точек нашего множества ). В идеале, однако , количество вершин в выходной выпуклой оболочке неизвестно в начале. Выполняется несколько проходов с увеличивающимися значениями , которые затем завершаются, когда (см. ниже о выборе параметра ). м {\displaystyle м} н {\displaystyle n} П {\displaystyle P} м = час {\displaystyle м=ч} час {\displaystyle ч} м {\displaystyle м} м час {\displaystyle m\geq h} м {\displaystyle м}

Алгоритм начинается с произвольного разбиения набора точек на подмножества , каждое из которых содержит не более точек; обратите внимание, что . П {\displaystyle P} К = н / м {\displaystyle K=\lceil n/m\rceil} ( В к ) к = 1 , 2 , . . . К {\displaystyle (Q_{k})_{k=1,2,...K}} м {\displaystyle м} К = О ( н / м ) {\displaystyle K=O(н/м)}

Для каждого подмножества он вычисляет выпуклую оболочку, , используя алгоритм (например, сканирование Грэма ), где — количество точек в подмножестве. Поскольку существуют подмножества точек, эта фаза занимает время. В к {\displaystyle Q_{k}} С к {\displaystyle C_{k}} О ( п бревно п ) {\displaystyle O(p\log p)} п {\displaystyle p} К {\displaystyle К} О ( м ) {\displaystyle O(м)} К О ( м бревно м ) = О ( н бревно м ) {\displaystyle K\cdot O(m\log m)=O(n\log m)}

Во время второй фазы выполняется марш Джарвиса с использованием предварительно вычисленных (мини) выпуклых оболочек, . На каждом шаге этого алгоритма марша Джарвиса у нас есть точка в выпуклой оболочке (в начале это может быть точка в с наименьшей координатой y, которая гарантированно находится в выпуклой оболочке ), и нужно найти точку такую, чтобы все остальные точки находились справа от линии [ необходимо разъяснение ] , где обозначение просто означает, что следующая точка, то есть , определяется как функция и . Выпуклая оболочка множества , , известна и содержит не более точек (перечисленных по часовой стрелке или против часовой стрелки), что позволяет производить вычисления за время с помощью бинарного поиска [ как? ] . Следовательно, вычисление для всех подмножеств может быть выполнено за время. Затем мы можем определить, используя ту же технику, которая обычно используется в марше Джарвиса, но рассматривая только точки (т. е. точки в мини-выпуклых оболочках) вместо всего множества . Для этих точек одна итерация марша Джарвиса равна , что пренебрежимо мало по сравнению с вычислением для всех подмножеств. Марш Джарвиса завершается, когда процесс повторяется несколько раз (потому что, согласно принципу работы марша Джарвиса, после не более чем итераций его самого внешнего цикла, где — количество точек в выпуклой оболочке , мы должны были найти выпуклую оболочку), следовательно, вторая фаза занимает время, эквивалентное времени , если близко к (см. ниже описание стратегии выбора, чтобы это было так). ( С к ) к = 1 , 2 , . . . К {\displaystyle (C_{k})_{k=1,2,...K}} п я {\displaystyle p_{i}} п я {\displaystyle p_{i}} П {\displaystyle P} П {\displaystyle P} п я + 1 = ф ( п я , П ) {\displaystyle p_{i+1}=f(p_{i},P)} П {\displaystyle P} п я п я + 1 {\displaystyle p_{i}p_{i+1}} п я + 1 = ф ( п я , П ) {\displaystyle p_{i+1}=f(p_{i},P)} п я + 1 {\displaystyle p_{i+1}} п я {\displaystyle p_{i}} П {\displaystyle P} В к {\displaystyle Q_{k}} С к {\displaystyle C_{k}} м {\displaystyle м} ф ( п я , В к ) {\displaystyle f(p_{i},Q_{k})} О ( бревно м ) {\displaystyle O(\log m)} ф ( п я , В к ) {\displaystyle f(p_{i},Q_{k})} К {\displaystyle К} О ( К бревно м ) {\displaystyle O(K\log m)} ф ( п я , П ) {\displaystyle f(p_{i},P)} ( ф ( п я , В к ) ) 1 к К {\displaystyle (f(p_{i},Q_{k}))_{1\leq k\leq K}} П {\displaystyle P} О ( К ) {\displaystyle O(К)} О ( час ) {\displaystyle O(h)} час {\displaystyle ч} час {\displaystyle ч} П {\displaystyle P} О ( К час бревно м ) {\displaystyle O(Kh\log m)} О ( н бревно час ) {\displaystyle O(n\log h)} м {\displaystyle м} час {\displaystyle ч} м {\displaystyle м}

Выполняя две описанные выше фазы, во времени вычисляется выпуклая оболочка точек . н {\displaystyle n} О ( н бревно час ) {\displaystyle O(n\log h)}

Выбор параметрам

Если для выбрано произвольное значение , может случиться, что . В этом случае после шагов во второй фазе мы прерываем марш Джарвиса , так как его прогон до конца занял бы слишком много времени. В этот момент время будет потрачено, а выпуклая оболочка не будет рассчитана. м {\displaystyle м} м < час {\displaystyle м<ч} м {\displaystyle м} О ( н бревно м ) {\displaystyle O(n\log m)}

Идея состоит в том, чтобы сделать несколько проходов алгоритма с увеличивающимися значениями ; каждый проход завершается (успешно или неудачно) со временем. Если увеличивается слишком медленно между проходами, число итераций может быть большим; с другой стороны, если оно увеличивается слишком быстро, первое, для которого алгоритм завершается успешно, может быть намного больше , и производить сложность . м {\displaystyle м} О ( н бревно м ) {\displaystyle O(n\log m)} м {\displaystyle м} м {\displaystyle м} час {\displaystyle ч} О ( н бревно м ) > О ( н бревно час ) {\displaystyle O(n\log m)>O(n\log h)}

Стратегия возведения в квадрат

Одна из возможных стратегий — возводить в квадрат значение на каждой итерации, вплоть до максимального значения (соответствующего разбиению на одноэлементные множества). [4] Начиная со значения 2, на итерации выбирается . В этом случае итерации выполняются, учитывая, что алгоритм завершается, как только мы имеем м {\displaystyle м} н {\displaystyle n} т {\displaystyle т} м = мин ( н , 2 2 т ) {\displaystyle m=\min \left(n,2^{2^{t}}\right)} О ( бревно бревно час ) {\displaystyle O(\log \log h)}

м = 2 2 т час бревно ( 2 2 т ) бревно час 2 т бревно час бревно 2 т бревно бревно час т бревно бревно час , {\displaystyle m=2^{2^{t}}\geq h\если и только если \log \left(2^{2^{t}}\right)\geq \log h\если и только если 2^{t}\geq \log h\если и только если \log {2^{t}}\geq \log {\log h}\если и только если t\geq \log {\log h},}

с логарифмом, взятым по основанию , а общее время работы алгоритма равно 2 {\displaystyle 2}

т = 0 бревно бревно час О ( н бревно ( 2 2 т ) ) = О ( н ) т = 0 бревно бревно час 2 т = О ( н 2 1 + бревно бревно час ) = О ( н бревно час ) . {\displaystyle \sum _{t=0}^{\lceil \log \log h\rceil }O\left(n\log \left(2^{2^{t}}\right)\right)=O(n)\sum _{t=0}^{\lceil \log \log h\rceil }2^{t}=O\left(n\cdot 2^{1+\lceil \log \log h\rceil }\right)=O(n\log h).}

В трех измерениях

Для обобщения этой конструкции для 3-мерного случая вместо сканирования Грэма следует использовать алгоритм вычисления 3-мерной выпуклой оболочки Препараты и Хонга, а также 3-мерную версию марша Джарвиса. Временная сложность остается . [1] О ( н бревно н ) {\displaystyle O(n\log n)} О ( н бревно час ) {\displaystyle O(n\log h)}

Псевдокод

В следующем псевдокоде текст в скобках и курсивом является комментариями. Для полного понимания следующего псевдокода рекомендуется, чтобы читатель уже был знаком с алгоритмами сканирования Грэма и марша Джарвиса для вычисления выпуклой оболочки, , набора точек, . С {\displaystyle С} П {\displaystyle P}

Вход: Задайте с помощью точек. П {\displaystyle P} н {\displaystyle n}
Выходные данные: множество точек , выпуклая оболочка . С {\displaystyle С} час {\displaystyle ч} П {\displaystyle P}
(Выберите точку, которая гарантированно находится в : например, точку с наименьшей координатой y.) П {\displaystyle P} С {\displaystyle С}
(Эта операция требует времени: например, мы можем просто выполнить итерацию .) О ( н ) {\displaystyle {\mathcal {O}}(n)} П {\displaystyle P}
п 1 := П я С К _ С Т А Р Т ( П ) {\displaystyle p_{1}:=ВЫБРАТЬ\_START(P)}
( используется в части марша Джарвиса этого алгоритма Чана, п 0 {\displaystyle p_{0}}
так что для вычисления второй точки, , в выпуклой оболочке .) п 2 {\displaystyle p_{2}} П {\displaystyle P}
(Примечание: это не точка .) п 0 {\displaystyle p_{0}} П {\displaystyle P}
(Более подробную информацию см. в комментариях рядом с соответствующей частью алгоритма Чана.)
п 0 := ( , 0 ) {\displaystyle p_{0}:=(-\infty,0)}
(Примечание: количество точек в конечной выпуклой оболочке неизвестно . ) час {\displaystyle ч} П {\displaystyle P}
(Это итерации, необходимые для нахождения значения , которое является оценкой .) м {\displaystyle м} час {\displaystyle ч}
( требуется для этого алгоритма Чана для нахождения выпуклой оболочки .) час м {\displaystyle h\leq m} П {\displaystyle P}
(Более конкретно, мы хотим , чтобы не выполнять слишком много ненужных итераций час м час 2 {\displaystyle h\leq m\leq h^{2}}
и поэтому временная сложность этого алгоритма Чана равна .) О ( н бревно час ) {\displaystyle {\mathcal {O}}(n\log h)}
(Как объяснялось выше в этой статье, используется стратегия, при которой для нахождения требуется максимум итераций .) бревно бревно н {\displaystyle \log \log n} м {\displaystyle м}
(Примечание: финал может быть не равен , но он никогда не будет меньше и больше .) м {\displaystyle м} час {\displaystyle ч} час {\displaystyle ч} час 2 {\displaystyle h^{2}}
(Тем не менее, этот алгоритм Чана останавливается после выполнения итераций самого внешнего цикла, час {\displaystyle ч}
то есть, даже если , он не выполняет итерации самого внешнего цикла.) м час {\displaystyle m\neq h} м {\displaystyle м}
(Более подробную информацию см. ниже в части марша Джарвиса этого алгоритма, где возвращается значение , если .) С {\displaystyle С} п я + 1 == п 1 {\displaystyle p_{i+1}==p_{1}}
для делать 1 т бревно бревно н {\displaystyle 1\leq t\leq \log \log n}
(Установите параметр для текущей итерации. Используется «схема возведения в квадрат», описанная выше в этой статье. м {\displaystyle м}
Существуют и другие схемы: например, «схема удвоения», где , для . м = 2 т {\displaystyle m=2^{t}} т = 1 , , бревно час {\displaystyle t=1,\dots ,\left\lceil \log h\right\rceil }
Однако если использовать «схему удвоения», то результирующая временная сложность этого алгоритма Чана составит .) О ( н бревно 2 час ) {\displaystyle {\mathcal {O}}(n\log ^{2}h)}
м := 2 2 т {\displaystyle м:=2^{2^{т}}}
(Инициализируйте пустой список (или массив) для хранения точек выпуклой оболочки по мере их нахождения.) П {\displaystyle P}
С := ( ) {\displaystyle C:=()}
А Д Д ( С , п 1 ) {\displaystyle ДОБАВИТЬ(C,p_{1})}
(Произвольно разбить множество точек на подмножества, каждое из которых содержит примерно по 10 элементов.) П {\displaystyle P} К = н м {\displaystyle K=\left\lceil {\frac {n}{m}}\right\rceil } м {\displaystyle м}
В 1 , В 2 , , В К := С П Л я Т ( П , м ) {\displaystyle Q_{1},Q_{2},\dots ,Q_{K}:=РАЗДЕЛИТЬ(P,m)}
(Вычислить выпуклую оболочку всех подмножеств точек, .) К {\displaystyle К} В 1 , В 2 , , В К {\displaystyle Q_{1},Q_{2},\точки ,Q_{K}}
(Это требует времени.) О ( К м бревно м ) = О ( н бревно м ) {\displaystyle {\mathcal {O}}(Km\log m) = {\mathcal {O}}(n\log m)}
Если , то временная сложность равна .) м час 2 {\displaystyle m\leq h^{2}} О ( н бревно час 2 ) = О ( н бревно час ) {\displaystyle {\mathcal {O}}(n\log h^{2}) = {\mathcal {O}}(n\log h)}
для делать 1 к К {\displaystyle 1\leq k\leq K}
(Вычислить выпуклую оболочку подмножества , , используя сканирование Грэма, что требует времени.) к {\displaystyle к} В к {\displaystyle Q_{k}} О ( м бревно м ) {\displaystyle {\mathcal {O}}(м\log м)}
( — выпуклая оболочка подмножества точек .) С к {\displaystyle C_{k}} В к {\displaystyle Q_{k}}
С к := Г Р А ЧАС А М _ С С А Н ( В к ) {\displaystyle C_{k}:=GRAHAM\_SCAN(Q_{k})}
(На этом этапе были вычислены выпуклые оболочки соответствующих подмножеств точек .) C 1 , C 2 , , C K {\displaystyle C_{1},C_{2},\dots ,C_{K}} Q 1 , Q 2 , , Q K {\displaystyle Q_{1},Q_{2},\dots ,Q_{K}}
(Теперь воспользуемся модифицированной версией алгоритма марша Джарвиса для вычисления выпуклой оболочки .) P {\displaystyle P}
(Марш Джарвиса выполняется за время, где — количество входных точек, а — количество точек в выпуклой оболочке.) O ( n h ) {\displaystyle {\mathcal {O}}(nh)} n {\displaystyle n} h {\displaystyle h}
(Учитывая, что алгоритм Jarvis March чувствителен к выходным данным , время его работы зависит от размера выпуклой оболочки. ) h {\displaystyle h}
(На практике это означает, что марш Джарвиса выполняет итерации своего самого внешнего цикла. h {\displaystyle h}
На каждой из этих итераций выполняется не более одного раза в течение самого внутреннего цикла.) n {\displaystyle n}
(Мы хотим , поэтому мы не хотим выполнять больше итераций в следующем внешнем цикле.) h m h 2 {\displaystyle h\leq m\leq h^{2}} m {\displaystyle m}
(Если ток меньше , т.е. , выпуклая оболочка не может быть найдена.) m {\displaystyle m} h {\displaystyle h} m < h {\displaystyle m<h} P {\displaystyle P}
(В этой модифицированной версии марша Джарвиса мы выполняем операцию внутри самого внутреннего цикла, которая требует времени. O ( log m ) {\displaystyle {\mathcal {O}}(\log m)}
Таким образом, общая временная сложность этой модифицированной версии составляет
O ( m K log m ) = O ( m n m log m ) = O ( n log m ) = O ( n log 2 2 t ) = O ( n 2 t ) . {\displaystyle {\mathcal {O}}(mK\log m)={\mathcal {O}}(m\left\lceil {\frac {n}{m}}\right\rceil \log m)={\mathcal {O}}(n\log m)={\mathcal {O}}(n\log 2^{2^{t}})={\mathcal {O}}(n2^{t}).}
Если , то временная сложность равна .) m h 2 {\displaystyle m\leq h^{2}} O ( n log h 2 ) = O ( n log h ) {\displaystyle {\mathcal {O}}(n\log h^{2})={\mathcal {O}}(n\log h)}
для делать 1 i m {\displaystyle 1\leq i\leq m}
(Примечание: здесь точка в выпуклой оболочке уже известна, то есть .) P {\displaystyle P} p 1 {\displaystyle p_{1}}
(В этом внутреннем цикле for вычисляются возможные следующие точки, которые будут находиться на выпуклой оболочке , , .) K {\displaystyle K} P {\displaystyle P} q i , 1 , q i , 2 , , q i , K {\displaystyle q_{i,1},q_{i,2},\dots ,q_{i,K}}
(Каждый из этих возможных следующих пунктов взят из разных источников : K {\displaystyle K} C k {\displaystyle C_{k}}
то есть является возможной следующей точкой на выпуклой оболочке , которая является частью выпуклой оболочки .) q i , k {\displaystyle q_{i,k}} P {\displaystyle P} C k {\displaystyle C_{k}}
(Примечание: зависит от : то есть для каждой итерации существуют возможные следующие точки, которые могут находиться на выпуклой оболочке .) q i , 1 , q i , 2 , , q i , K {\displaystyle q_{i,1},q_{i,2},\dots ,q_{i,K}} i {\displaystyle i} i {\displaystyle i} K {\displaystyle K} P {\displaystyle P}
(Примечание: на каждой итерации только одна из точек среди добавляется к выпуклой оболочке .) i {\displaystyle i} q i , 1 , q i , 2 , , q i , K {\displaystyle q_{i,1},q_{i,2},\dots ,q_{i,K}} P {\displaystyle P}
для делать 1 k K {\displaystyle 1\leq k\leq K}
( находит точку , в которой угол максимален [ почему? ] , J A R V I S _ B I N A R Y _ S E A R C H {\displaystyle JARVIS\_BINARY\_SEARCH} d C k {\displaystyle d\in C_{k}} p i 1 p i d {\displaystyle \measuredangle p_{i-1}p_{i}d}
где - угол между векторами и . Такой хранится в .) p i 1 p i d {\displaystyle \measuredangle p_{i-1}p_{i}d} p i p i 1 {\displaystyle {\overrightarrow {p_{i}p_{i-1}}}} p i d {\displaystyle {\overrightarrow {p_{i}d}}} d {\displaystyle d} q i , k {\displaystyle q_{i,k}}
(Углы не обязательно рассчитывать напрямую: можно использовать тест на ориентацию [ как? ] .)
( можно выполнить вовремя [ как? ] .) J A R V I S _ B I N A R Y _ S E A R C H {\displaystyle JARVIS\_BINARY\_SEARCH} O ( log m ) {\displaystyle {\mathcal {O}}(\log m)}
(Примечание : на итерации известно и является точкой в ​​выпуклой оболочке : i = 1 {\displaystyle i=1} p i 1 = p 0 = ( , 0 ) {\displaystyle p_{i-1}=p_{0}=(-\infty ,0)} p 1 {\displaystyle p_{1}} P {\displaystyle P}
в данном случае это точка с наименьшей координатой y.) P {\displaystyle P}
q i , k := J A R V I S _ B I N A R Y _ S E A R C H ( p i 1 , p i , C k ) {\displaystyle q_{i,k}:=JARVIS\_BINARY\_SEARCH(p_{i-1},p_{i},C_{k})}
(Выберите точку , которая максимизирует угол [ почему? ] , чтобы она стала следующей точкой на выпуклой оболочке .) z { q i , 1 , q i , 2 , , q i , K } {\displaystyle z\in \{q_{i,1},q_{i,2},\dots ,q_{i,K}\}} p i 1 p i z {\displaystyle \measuredangle p_{i-1}p_{i}z} P {\displaystyle P}
p i + 1 := J A R V I S _ N E X T _ C H _ P O I N T ( p i 1 , p i , ( q i , 1 , q i , 2 , , q i , K ) ) {\displaystyle p_{i+1}:=JARVIS\_NEXT\_CH\_POINT(p_{i-1},p_{i},(q_{i,1},q_{i,2},\dots ,q_{i,K}))}
(Марш Джарвиса заканчивается, когда следующая выбранная точка на выпуклой оболочке, , становится начальной точкой, .) p i + 1 {\displaystyle p_{i+1}} p 1 {\displaystyle p_{1}}
если p i + 1 == p 1 {\displaystyle p_{i+1}==p_{1}}
(Вернуть выпуклую оболочку , содержащую точки.) P {\displaystyle P} i = h {\displaystyle i=h}
(Примечание: конечно, нет необходимости возвращать , что равно .) p i + 1 {\displaystyle p_{i+1}} p 1 {\displaystyle p_{1}}
возвращаться C := ( p 1 , p 2 , , p i ) {\displaystyle C:=(p_{1},p_{2},\dots ,p_{i})}
еще
A D D ( C , p i + 1 ) {\displaystyle ADD(C,p_{i+1})}
(Если после итераций не найдена точка такая, что , то .) m {\displaystyle m} p i + 1 {\displaystyle p_{i+1}} p i + 1 == p 1 {\displaystyle p_{i+1}==p_{1}} m < h {\displaystyle m<h}
(Нам нужно начать заново с более высоким значением .) m {\displaystyle m}

Выполнение

В статье Чана содержится несколько предложений, которые могут улучшить практическую эффективность алгоритма, например:

  • При вычислении выпуклых оболочек подмножеств исключите из рассмотрения в последующих вычислениях точки, не входящие в выпуклую оболочку.
  • Выпуклые оболочки более крупных наборов точек можно получить путем слияния ранее вычисленных выпуклых оболочек, а не пересчета с нуля.
  • С вышеизложенной идеей, доминирующая стоимость алгоритма заключается в предварительной обработке, т. е. вычислении выпуклых оболочек групп. Чтобы уменьшить эту стоимость, мы можем рассмотреть повторное использование оболочек, вычисленных из предыдущей итерации, и их слияние по мере увеличения размера группы.

Расширения

В статье Чана описываются и другие проблемы, известные алгоритмы которых можно сделать максимально чувствительными к выходным данным, используя его методику, например:

  • Вычисление нижней огибающей набора отрезков прямой, которая определяется как нижняя граница неограниченной трапеции , образованной пересечениями. L ( S ) {\displaystyle L(S)} S {\displaystyle S} n {\displaystyle n}
  • Хершбергер [5] дал алгоритм, который можно ускорить до , где h — число ребер в конверте O ( n log n ) {\displaystyle O(n\log n)} O ( n log h ) {\displaystyle O(n\log h)}
  • Построение выходных чувствительных алгоритмов для многомерных выпуклых оболочек. С использованием группирующих точек и эффективных структур данных сложность может быть достигнута при условии, что h имеет полиномиальный порядок в . O ( n log h ) {\displaystyle O(n\log h)} n {\displaystyle n}

Смотрите также

Ссылки

  1. ^ ab Chan, Timothy M. (1996). «Оптимальные алгоритмы выпуклой оболочки, чувствительные к выходу, в двух и трех измерениях». Дискретная и вычислительная геометрия . 16 (4): 361–368. doi : 10.1007/BF02712873 .
  2. ^ Нильсен, Франк (2000). «Группировка и запросы: парадигма получения чувствительных к выходу алгоритмов». Дискретная и вычислительная геометрия . Конспект лекций по информатике. Том 1763. С. 250–257. doi : 10.1007/978-3-540-46515-7_21 . ISBN 978-3-540-67181-7.
  3. ^ Фрэнк Нильсен. «Адаптивная вычислительная геометрия». Кандидатская диссертация, INRIA , 1996.
  4. ^ Шазелл, Бернард ; Матоушек, Йиржи (1995). «Дерандомизация алгоритма выпуклой оболочки, чувствительного к выходу, в трех измерениях». Computational Geometry . 5 : 27–32. doi : 10.1016/0925-7721(94)00018-Q .
  5. ^ Хершбергер, Джон (1989). «Нахождение верхней огибающей n отрезков линии за время O(n log n)». Information Processing Letters . 33 (4): 169–174. doi :10.1016/0020-0190(89)90136-1.
Retrieved from "https://en.wikipedia.org/w/index.php?title=Chan%27s_algorithm&oldid=1216100766"