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


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.