Який найефективніший спосіб перевірити два цілих діапазони на перекриття?


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 вірно, отже, відбувається перекриття.
— 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 Ні, але я впевнений, що ви можете знайти інформацію самостійно
— Ashton Six

6
Крім того, зауважте, що min(x2,y2) - max(x1,y1)передбачена сума перекриття, якщо вам це потрібно.
— користувач1556435

59

Це може легко перекрутити нормальний людський мозок, тому я знайшов візуальний підхід, який буде легше зрозуміти:

перекривають божевілля

le Пояснення

Якщо два діапазони "занадто жирними", щоб вміститися в проріз, який точно відповідає сумі ширини обох, то вони перекриваються.

Для діапазонів [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 // де я використовую 'менше ніж' замість «більше ніж».
— Park 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, згенерований код в основному однаковий для простого виразу проти коду в цій відповіді.
— Søren Løvborg


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.

Тому умова зробити 2 діапазони перекриттями:, not(y2 < x1 or x2 < y1)що еквівалентно y2 >= x1 and x2 >= y1(те саме, що приймається Саймоном відповідь).


Виглядає так само, як відповів @damluar (2 березня '16 о 17:36)
— Накілон

0

У вас найефективніше представництво - це найменший мінімум, який потрібно перевірити, якщо ви точно не знаєте, що x1 <x2 і т. Д., То використовуйте рішення, які надали інші.

Вам, мабуть, слід зазначити, що деякі компілятори фактично оптимізують це для вас - повернувшись, як тільки будь-який із цих 4 виразів повернеться істинним. Якщо один повернеться істинним, таким буде і кінцевий результат - тому інші перевірки можна просто пропустити.


2
Усі компілятори будуть. Усі (наскільки мені відомо) мови, що використовуються в даний час з синтаксисом стилю C (C, C ++, C #, Java тощо), використовують булінові оператори з коротким замиканням, і це частина різних стандартів, які регулюють ці мови. Якщо результат лівого значення достатній для визначення результату операції, значення правої руки не оцінюється.
— Джонатан Грінспан

1
Позначити Н - компілятор буде пропускати другий пункт, якщо він може: так що якщо у вас є функція, яка говорить: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Всередині \ n"); printf ("i є% d \ n", i); Foo (2) надрукує: Всередині i is 0, а Foo (4) надрукує: i is 1 (перевірено на gcc 4.4.3, але я покладався на цю поведінку і на деякий некрасивий код в icc)
— J Teller

0

У мене справа інша. Я хочу перевірити, що два часові діапазони перекриваються. не повинно бути перекриття одиниці часу. ось перехід на реалізацію.

    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 зростає / обманюється (в будь-якому випадку, це відсортовано) - немає необхідності в циклі, потрібно лише перевірити елементи голови та хвоста. Я вважаю за краще рішення min / max - просто тому, що його легше читати, коли пізніше ти повернешся до коду.
— Марк Н

12
-1: це не мікрооптимізація; це вибір відповідного алгоритму. Ваш алгоритм - O (n), коли є простий вибір O (1).
— Саймон Нікерсон

Це те, що відбувається, коли "передчасна оптимізація - корінь усього зла" стає непорушним релігійним догмом для невмілих замість напівсерйозного зауваження щодо якоїсь випадкової поведінки.
— rghome
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.