Алгоритм определения того, имеют ли две периодические последовательности интервалов непустое пересечение - PullRequest
2 голосов
/ 04 сентября 2011

Я ищу алгоритм, который, учитывая естественные параметры l1, n1, m1, l2, n2, m2 и размер, отвечает «истина» тогда и только тогда, когда существуют натуральные числа k1, k2, r1, r2, такие что:

l1 + k1 * m1 + r1 = l2 + k2 * m2 + r2

с ограничениями k1 <= n1, k2 <= n2, r1 <размер, r2 <размер и r1 <> r2.

Очевидное решение является линейным по min (n1, n2). Я ищу что-то более эффективное.

Контекст

Я пытаюсь реализовать в статическом анализаторе проверку на правило C99 6.5.16.1:3

.

Если значение, хранящееся в объекте, читается из другого объекта который каким-либо образом перекрывает хранилище первого объекта, затем перекрытие должно быть точным [...] в противном случае, поведение не определено.

Когда анализатор встречает присвоение *p1 = *p2;, где p1 и p2 могут указывать на один и тот же блок, он должен проверить, чтобы зоны, на которые указывают p1 и p2, не перекрывались способом, запрещенным вышеуказанное правило. Приведенный выше параметр «size» соответствует размеру типа, на который указывают p1 и p2. Этот размер статически известен. Смещения, на которые, как известно, p1 указывают внутри блока, представляются в виде набора l1 + k1 * m1 с фиксированными l1 и m1, известными натуральными целыми числами и k1, варьирующимися от 0 до n1 (n1 также фиксировано и известно). Аналогично, известно, что смещения p2 имеют вид l2 + k2 * m2 для некоторых известных l2, m2 и n2. Уравнение l1 + k1 * m1 + r1 = l2 + k2 * m2 + r2 соответствует существованию некоторых перекрывающихся смещений. Условие r1 <> r2 соответствует случаю, когда перекрытие не является точным, когда анализатор должен предупредить.

Ответы [ 2 ]

3 голосов
/ 04 сентября 2011

Кажется, вы ищете решение для линейной системы сравнений. Китайская теорема об остатках должна быть применима.Он не будет применять проверку ваших границ, но если он найдет решение, вы можете проверить границы самостоятельно.

РЕДАКТИРОВАТЬ: Забудьте CRT.

Предполагая size <= m1 и size <= m2,смоделируйте нижние (включительно) и высокие (исключительные) края областей памяти как линейные отношения:

addr1low = l1 + k1*m1
addr1high = addr1low + size = l1 + k1*m1 + size
addr2low = l2 + k2*m2
addr2high = addr2low + size = l2 + k2*m2 + size

Вы хотите знать, существует ли диапазон k1, k2 такой, что addr1low < addr2low < addr1high или addr1low < addr2high < addr1high,Обратите внимание на исключительные неравенства;таким образом мы избегаем точно перекрывающихся диапазонов.

Предположим, m1 = m2 = m.Обратите внимание:

addr1low < addr2low
l1 + k1*m < l2 + k2*m
(k1 - k2) * m < l2 - l1
k1 - k2 < (l2 - l1) / m

addr2low < addr1high
l2 + k2*m < l1 + k1*m + size
l2 - l1 < (k1 - k2) * m + size
(l2 - l1 - size) < (k1 - k2) * m
(l2 - l1 - size) / m < k1 - k2

Вышеуказанная прогрессия обратима.Предполагая, что k1, k2 может быть 0, -n2 <= k1 - k2 <= n1.Если в диапазоне от (l2 - l1 - size) / m до (l2 - l1) / m есть целое число, то система удерживается и происходит наложение.Это если ceil(max((l2 - l1 - size) / m, -n2)) <= floor(min((l2 - l1) / m, n1)).Другой случай (addr1low < addr2high < addr1high) происходит аналогично:

addr1low < addr2high
l1 + k1*m < l2 + k2*m + size
// ..
(l1 - l2 - size) / m < k2 - k1

addr2high < addr1high
addr2low + size < addr1low + size
addr2low < addr1low
// ..
k2 - k1 < (l1 - l2) / m

Теперь тест становится ceil(max((l1 - l2 - size) / m, -n1)) <= floor(min((l1 - l2) / m, n2)).

Теперь рассмотрим m1 <> m1, а без потери общности возьмем m1 < m2.

Обрабатывая переменные как непрерывные, решите пересечения:

addr1low < addr2low
l1 + k*m1 < l2 + k*m2
(l1 - l2) < k * (m2 - m1)
(l1 - l2) / (m2 - m1) < k

addr2low < addr1high
l2 + k*m2 < l1 + k*m1 + size
l2 - l1 - size < k * (m1 - m2)
(l2 - l1 - size) / (m1 - m2) > k  // m1 - m2 < 0

Опять же, шаги обратимы, поэтому любое целое число k < min(n1, n2), удовлетворяющее границам, заставит систему удержаться.То есть, если ceil(max((l1 - l2) / (m2 - m1), 0)) <= floor(min((l2 - l1 - size) / (m1 - m2), n1, n2)).Другой случай:

addr1low < addr2high
l1 + k*m1 < l2 + k*m2 + size
l1 - l2 - size < k * (m2 - m1)
(l1 - l2 - size) / (m2 - m1) < k

addr2high < addr1high
addr2low + size < addr1low + size
addr2low < addr1low
l2 + k*m2 < l1 + k*m1
l2 - l1 < k * (m1 - m2)
(l2 - l1) / (m1 - m2) > k   // m1 - m2 < 0

Здесь тест становится ceil(max((l1 - l2 - size) / (m2 - m1), 0)) <= floor(min((l2 - l1) / (m1 - m2), n1, n2)).

Окончательный псевдокод может выглядеть примерно так:

intersectos?(l1, n1, m1, l2, n2, m2, size) {
  if (m1 == m2) {
    return ceil(max((l2 - l1 - size) / m, -n2)) <= floor(min((l2 - l1) / m, n1)) ||
           ceil(max((l1 - l2 - size) / m, -n1)) <= floor(min((l1 - l2) / m, n2));
  }

  if (m1 > m2) {
    swap the arguments
  }

  return ceil(max((l1 - l2) / (m2 - m1), 0)) <= floor(min((l2 - l1 - size) / (m1 - m2), n1, n2)) ||
         ceil(max((l1 - l2 - size) / (m2 - m1), 0)) <= floor(min((l2 - l1) / (m1 - m2), n1, n2));
}
2 голосов
/ 25 апреля 2016

1. Небольшое улучшение очевидного алгоритма

Взаимное расположение p1 и p2 является периодическим с периодом, равным lcm (m1, m2) = m1 * m2 / gcd (m1, m2). В очевидном решении предположим, что вы перебираете k1 от 0 до n1. Благодаря упомянутому шаблону повторения вы можете остановиться, как только достигнете м2 / г / д (м1, м2). Я предполагаю, что вы уже сократили (l1, n1) только до пересечения.

2. Более хитрый подход

Давайте переформулируем вопрос: минимальное ненулевое расстояние между p1 и p2 меньше размера?

Сначала давайте рассмотрим p1-p2. Затем мы добавим p2-p1 к смеси.

Итак, мы хотим найти минимальное ненулевое значение для

p1 - p2 = (l1 + k1 * m1) - (l2 + k2 * m2) = (l1-l2) + (k1 * m1 - k2 * m2).

Пусть d = gcd (m1, m2). Если мы разрешим k1 и k2 принимать произвольные целочисленные значения, то набор всех возможных значений k1 * m1 - k2 * m2 точно такой же, как набор всех возможных значений k * d для произвольного целого числа k.

Следовательно, минимальное ненулевое значение (l1-l2) + (k1 * m1 - k2 * m2) равно (l1-l2) mod d, если оно ненулевое, или d в противном случае. Здесь mod дает положительный результат, даже если (l1-l2) отрицателен.

Найдем минимальные такие k1 и k2. Расширенный евклидов алгоритм дает нам a1 и a2 (возможно, отрицательные) такие, что d = a1 * m1 - a2 * m2. Решение является уникальным до сложения 0 = (м2 / день) * м2 - (м2 / день) * м2. Тогда

(l1-l2) mod d = (l1-l2) + k * d

(l1-l2) mod d = (l1-l2) + k * (a1 * m1 - a2 * m2)

(l1-l2) mod d = (l1-l2) + (k * a1) * m1 - (k * a2) * m2

Добавьте или вычтите 0 = (m2 / d) * m1 - (m1 / d) * m2 достаточное количество раз, чтобы коэффициенты m1 и m2 были положительными и минимальными при этом. Это дает вам положение, когда p2 впервые попадает в p1 на расстоянии (l1-l2) mod d.

Если только что найденные k1 и k2 находятся в границах и (l1-l2) mod d не равен нулю, то это дает положительный ответ на вопрос. В противном случае повторите то же самое для (l1-l2) mod d + d. И продолжайте повторять, пока не достигнете размера.

Затем повторите для p2-p1 вместо p1-p2.

Если ни один из шагов не дал положительного ответа, то ответ отрицательный.

Сложность этого алгоритма O (размер / gcd (m1, m2)).

Какой алгоритм предпочитать в каждом конкретном случае, зависит от конкретных чисел. Вычисление gcd тоже не бесплатно (O (log (min (m1, m2)))).

...