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


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.