Какой самый эффективный способ проверить два целочисленных диапазона на совпадение?


252

С учетом двух целочисленных диапазонов [x1: x2] и [y1: y2], где x1 ≤ x2 и y1 ≤ y2, какой самый эффективный способ проверить, есть ли какое-либо перекрытие двух диапазонов?

Простая реализация выглядит следующим образом:

bool testOverlap(int x1, int x2, int y1, int y2) {
  return (x1 >= y1 && x1 <= y2) ||
         (x2 >= y1 && x2 <= y2) ||
         (y1 >= x1 && y1 <= x2) ||
         (y2 >= x1 && y2 <= x2);
}

Но я ожидаю, что есть более эффективные способы вычислить это.

Какой метод будет наиболее эффективным с точки зрения наименьшего количества операций.


Для некоторых это может быть интересно связано - stackoverflow.com/q/17138760/104380
vsync

Ответы:


454

Что означает перекрытие диапазонов? Это означает, что существует некоторое число C, которое находится в обоих диапазонах, т.е.

x1 <= C <= x2

и

y1 <= C <= y2

Теперь, если нам позволят предположить, что диапазоны правильно сформированы (так что x1 <= x2 и y1 <= y2), то достаточно проверить

x1 <= y2 && y1 <= x2

1
Я считаю, что так и должно быть x1 <= y2 && y1 >= x2, нет?
Дэвид Бек,

8
@DavidBeck: нет, если y1> x2, то диапазоны определенно не перекрываются (например, рассмотрим [1: 2] и [3: 4]: y1 = 3 и x2 = 2, поэтому y1> x2, но перекрытия нет) ,
Саймон Никерсон

8
это было бы лучшим ответом, если бы вы объяснили рассуждения немного больше
шош

2
@ Vineet Deoraj - Почему вы думаете, что это не работает? x1 = 1, y1 = 1, x2 = 1, y2 = 1, поэтому x1 <= y2 && y1 <= x2 имеет значение true, поэтому существует перекрытие.
DCP

2
Объяснение здесь: stackoverflow.com/questions/325933/…
Alex

138

Даны два диапазона [x1, x2], [y1, y2]

def is_overlapping(x1,x2,y1,y2):
    return max(x1,y1) <= min(x2,y2)

4
@ uyuyuy99 - только не так эффективно, потому что, когда эта проверка выполняется много раз в секунду, вызов функции - это то, чего вы хотели бы избежать, и выполняйте столько же математики самостоятельно, придерживайтесь основного
vsync

7
@vsync Современные браузеры будут встроены и оптимизировать функции, такие как Math.max, не должно быть заметного влияния на производительность.
Эштон Шесть

1
@AshtonWar - интересно. у вас есть статья, объясняющая, что встраивается, а что нет?
vsync

@vsync Нет, но я уверен, что вы сами сможете найти эту информацию
Эштон Сикс

6
Кроме того, обратите внимание, что это min(x2,y2) - max(x1,y1)обеспечивает количество совпадений, если вам это нужно.
user1556435

59

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

перекрывать безумие

ле Объяснение

Если два диапазона «слишком толстые», чтобы поместиться в слоте, который в точности равен сумме ширины обоих, они перекрываются.

Для диапазонов [a1, a2]и [b1, b2]это было бы:

/**
 * we are testing for:
 *     max point - min point < w1 + w2    
 **/
if max(a2, b2) - min(a1, b1) < (a2 - a1) + (b2 - b1) {
  // too fat -- they overlap!
}

3
Есть больше случаев, чем изображено на ваших фотографиях. Например, что если w2 начинается до w1 и заканчивается после w1?
WilliamKF

7
@WilliamKF логика верна
FloatingRock

2
Согласен, но я думаю, что это может помочь предоставить третью картину.
WilliamKF

3
@WilliamKF, тогда вам нужно больше изображений, есть 16 различных комбинаций, в которых можно разместить 2 диапазона ...
Питер

3
Будьте осторожны, если вы используете этот метод, потому что сумма a2 - a1 + b2 - b1может переполниться. Чтобы исправить это, измените формулу на max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1, что упрощает max(a1, b1) < min(a2, b2)сохранение некоторой арифметики и предотвращение возможных переполнений (ответ AX-Labs ниже). В особом случае, когда вы знаете b2-b1=a2-a1, есть еще одна полезная перестановка формулы FloatingRock max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1, которая становится abs(b1-a1) < a2 - a1.
Паоло Бонзини

44

Отличный ответ от Саймона , но мне было проще подумать об обратном случае.

Когда 2 диапазона не перекрываются? Они не перекрываются, когда один из них начинается после того, как другой заканчивается:

dont_overlap = x2 < y1 || x1 > y2

Теперь легко выразить, когда они перекрываются:

overlap = !dont_overlap = !(x2 < y1 || x1 > y2) = (x2 >= y1 && x1 <= y2)

1
Мне проще понять выражение: x2 <y1 || y2 <x1 // где я использую «меньше чем» вместо «больше чем».
Парк JongBum

26

Вычитание минимума из концов диапазонов из максимума начала, кажется, делает свое дело. Если результат меньше или равен нулю, мы имеем перекрытие. Это хорошо визуализирует это:

введите описание изображения здесь


2
Это распространяется на все случаи
user3290180

10

Я предполагаю, что вопрос был о самом быстром, а не самом коротком коде. Самая быстрая версия должна избегать веток, поэтому мы можем написать что-то вроде этого:

для простого случая:

static inline bool check_ov1(int x1, int x2, int y1, int y2){
    // insetead of x1 < y2 && y1 < x2
    return (bool)(((unsigned int)((y1-x2)&(x1-y2))) >> (sizeof(int)*8-1));
};

или, для этого случая:

static inline bool check_ov2(int x1, int x2, int y1, int y2){
    // insetead of x1 <= y2 && y1 <= x2
    return (bool)((((unsigned int)((x2-y1)|(y2-x1))) >> (sizeof(int)*8-1))^1);
};

7
Верьте в свой компилятор. Выражение x1 <= y2 && y1 <= x2 также не содержит никаких ветвей , предполагая достаточно компетентную архитектуру компилятора и процессора (даже в 2010 году). На самом деле, на x86 сгенерированный код в основном идентичен для простого выражения и кода в этом ответе.
Сорен Левборг


4

Если вы имели дело с заданными двумя диапазонами [x1:x2]и диапазонами [y1:y2]естественного / антиестественного порядка одновременно, где:

  • естественный порядок: x1 <= x2 && y1 <= y2или
  • анти-природный порядок: x1 >= x2 && y1 >= y2

тогда вы можете использовать это, чтобы проверить:

они перекрываются <=> (y2 - x1) * (x2 - y1) >= 0

где задействованы только четыре операции:

  • два вычитания
  • одно умножение
  • одно сравнение

1

Если кто-то ищет однострочник, который вычисляет фактическое перекрытие:

int overlap = ( x2 > y1 || y2 < x1 ) ? 0 : (y2 >= y1 && x2 <= y1 ? y1 : y2) - ( x2 <= x1 && y2 >= x1 ? x1 : x2) + 1; //max 11 operations

Если вы хотите на пару операций меньше, но на пару больше переменных:

bool b1 = x2 <= y1;
bool b2 = y2 >= x1;
int overlap = ( !b1 || !b2 ) ? 0 : (y2 >= y1 && b1 ? y1 : y2) - ( x2 <= x1 && b2 ? x1 : x2) + 1; // max 9 operations

1

Подумайте наоборот : как сделать так, чтобы 2 диапазона не перекрывались ? Учитывая [x1, x2], то [y1, y2]должно быть снаружи [x1, x2] , т. y1 < y2 < x1 or x2 < y1 < y2Е. Что эквивалентно y2 < x1 or x2 < y1.

Следовательно, условие перекрытия двух диапазонов: not(y2 < x1 or x2 < y1)эквивалентно y2 >= x1 and x2 >= y1(то же самое, что принятый ответ Саймона).


Выглядит так же, как и то, что ответил @damluar (2 марта '16 в 17:36)
Накилон

0

У вас уже есть самое эффективное представление - это тот минимум, который необходимо проверить, если вы не уверены, что x1 <x2 и т. Д., А затем используйте решения, которые предоставили другие.

Вы, вероятно, должны заметить, что некоторые компиляторы на самом деле оптимизируют это для вас - возвращая, как только любое из этих 4 выражений вернет true. Если один из них вернет true, конечный результат будет таким же, поэтому другие проверки можно просто пропустить.


2
Все компиляторы будут. На всех (насколько мне известно) используемых в настоящее время языках с синтаксисом в стиле C (C, C ++, C #, Java и т. Д.) Используются короткозамкнутые логические операторы, и это является частью различных стандартов, регулирующих эти языки. Если результат левого значения является достаточным для определения результата операции, правое значение не оценивается.
Джонатан Гринспан

1
Отметьте H - компилятор пропустит второе предложение, если сможет: так, если у вас есть функция, которая говорит: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Inside \ n"); printf («i - это% d \ n», i); Foo (2) выведет: внутри i равно 0, а Foo (4) выведет: i равно 1 (протестировано на gcc 4.4.3, но я полагался на это поведение и для некоторого некрасивого кода в icc)
J Teller

0

Мой случай другой. Я хочу проверить два временных диапазона перекрытия. не должно быть частичного совпадения времени. вот реализация Go.

    func CheckRange(as, ae, bs, be int) bool {
    return (as >= be) != (ae > bs)
    }

Контрольные примеры

if CheckRange(2, 8, 2, 4) != true {
        t.Error("Expected 2,8,2,4 to equal TRUE")
    }

    if CheckRange(2, 8, 2, 4) != true {
        t.Error("Expected 2,8,2,4 to equal TRUE")
    }

    if CheckRange(2, 8, 6, 9) != true {
        t.Error("Expected 2,8,6,9 to equal TRUE")
    }

    if CheckRange(2, 8, 8, 9) != false {
        t.Error("Expected 2,8,8,9 to equal FALSE")
    }

    if CheckRange(2, 8, 4, 6) != true {
        t.Error("Expected 2,8,4,6 to equal TRUE")
    }

    if CheckRange(2, 8, 1, 9) != true {
        t.Error("Expected 2,8,1,9 to equal TRUE")
    }

    if CheckRange(4, 8, 1, 3) != false {
        t.Error("Expected 4,8,1,3 to equal FALSE")
    }

    if CheckRange(4, 8, 1, 4) != false {
        t.Error("Expected 4,8,1,4 to equal FALSE")
    }

    if CheckRange(2, 5, 6, 9) != false {
        t.Error("Expected 2,5,6,9 to equal FALSE")
    }

    if CheckRange(2, 5, 5, 9) != false {
        t.Error("Expected 2,5,5,9 to equal FALSE")
    }

Вы можете видеть, что есть XOR шаблон в сравнении границ


-10

Вот моя версия:

int xmin = min(x1,x2)
  , xmax = max(x1,x2)
  , ymin = min(y1,y2)
  , ymax = max(y1,y2);

for (int i = xmin; i < xmax; ++i)
    if (ymin <= i && i <= ymax)
        return true;

return false;

Если вы не используете какой-либо высокопроизводительный инструмент проверки диапазона для миллиардов широко разнесенных целых чисел, наши версии должны работать аналогично. Я хочу сказать, что это микрооптимизация.


Я думаю, что вы прошли здесь спецификацию. Предполагается, что от x1 до x2 идет по возрастанию / убыванию (в любом случае, он отсортирован) - петля не нужна, вам нужно только проверить элементы head и tail. Однако я предпочитаю решение min / max - просто потому, что его легче читать, когда вы вернетесь к коду позже.
Марк Х

12
-1: это не микрооптимизация; это выбор подходящего алгоритма. Ваш алгоритм O (n), когда есть простой выбор O (1).
Саймон Никерсон

Это то, что происходит, когда «преждевременная оптимизация является корнем всего зла» становится нерушимым религиозным принципом для неумелых людей вместо полусерьезного замечания о некой случайной модели поведения.
rghome
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.