Я не знаю о существующем алгоритме (что не означает, что его нет), но вот один, который я придумал.
Как уже упоминал @Michael Kenzel, числа не увеличиваются монотонно, что делает все очень сложным.
Но мы можем заметить, что мы можем развернуть круг на числовой линии.
Тогда каждый интервал появляется бесконечно много раз с периодом 2π.
Давайте сначала определим нормализовать операцию следующим образом:
normalize([a, b]):
if (a > b):
a -= 2π
Используя эту операцию, мы развертываем оба наших интервала в [-2π, 2π] часть числовой линии.
Пример интервалов:
[2, 5] -> [2, 5]
[4, π] -> [-2, π]
Два интервала на окружности могут перекрываться не более 2 раз.
(У меня нет правильного доказательства этого, но интуитивно понятно: перекрытие начинается, когда один интервал начался, а другой не закончился. Это может произойти только один раз в числовой строке и дважды в нашем случае.)
Просто глядя на нормализованные интервалы, мы можем пропустить одно из совпадений. В приведенном выше примере мы обнаружили бы перекрытие [2, π] и пропуск [4, 5]. Это происходит потому, что мы развернули исходные интервалы не на [0, 2π], а на вдвое большую часть числовой линии, [-2π, 2π].
Чтобы исправить , для этого мы можем, например, взять часть, которая падает на отрицательную линию, и сдвинуть ее на 2π вправо, таким образом, имея все части в оригинале [0, 2π ]. Но это вычислительно неэффективно, так как в худшем случае нам придется тестировать 2 фрагмента за один интервал против 2 фрагментов другого интервала - всего 4 операции.
Вот иллюстрация такого неудачного примера, который потребует 4 сравнений:
Если мы хотим быть немного более эффективными, мы попытаемся выполнить только 2 операции интервал-интервал. Нам не нужно больше, поскольку будет не более двух перекрытий.
Поскольку интервалы повторяются бесконечно на числовой линии с периодом 2π, мы можем просто взять 2 соседних дубликата первого интервала и сравнить их со вторым.
Чтобы убедиться, что второй интервал будет, так сказать, между этими дубликатами, мы можем взять тот, который начинается раньше, и добавить 2π к его обоим концам. Или вычтите 2π из того, что начинается позже.
Будет не более двух перекрытий, которые затем могут быть возвращены к интервалу [0, 2π] путем сложения / вычитания 2π.
В нашем исходном примере это будет выглядеть так:
Подводя итог:
given [a, b], [c, d]
[A, B] = normalize([a, b])
[C, D] = normalize([c, d])
if (A < C):
[E, F] = [A + 2π, B + 2π]
else:
[E, F] = [A - 2π, B - 2π]
I_1 = intersect([A, B], [C, D])
I_2 = intersect([E, F], [C, D])
bring I_1 and I_2 back to the [0, 2π]
Я думаю, что не пропустил ни одного углового случая, но не стесняйтесь указывать на любую ошибку в моей логике.