388

Given two inclusive ranges [x1:x2] and [y1:y2], where x1 ≤ x2 and y1 ≤ y2, what is the most efficient way to test whether there is any overlap of the two ranges?

A simple implementation is as follows:

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);
}

But I expect there are more efficient ways to compute this.

What method would be the most efficient in terms of fewest operations?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
WilliamKF
  • 41,123
  • 68
  • 193
  • 295

17 Answers17

669

What does it mean for the ranges to overlap? It means there exists some number C which is in both ranges, i.e.

x1 <= C <= x2

and

y1 <= C <= y2

To avoid confusion, considering the ranges are: [x1:x2] and [y1:y2]

Now, if we are allowed to assume that the ranges are well-formed (so that x1 <= x2 and y1 <= y2) then it is sufficient to test

x1 <= y2 && y1 <= x2

OR

(StartA <= EndB) and (EndA >= StartB)

Kakshil Shah
  • 3,466
  • 1
  • 17
  • 31
Simon Nickerson
  • 42,159
  • 20
  • 102
  • 127
  • 4
    I believe it should be `x1 <= y2 && y1 >= x2`, no? – David Beck Mar 27 '13 at 22:40
  • 9
    @DavidBeck: no, if y1 > x2 then the ranges definitely don't overlap (e.g. consider [1:2] and [3:4]: y1 = 3 and x2 = 2, so y1 > x2, but there's no overlap). – Simon Nickerson Mar 28 '13 at 09:14
  • is the a good way to solve this when you have many ranges? some efficient way to crunch all the ranges together and then compare that to `C`? – vsync Mar 30 '16 at 14:09
  • @SimonNickerson This doesn't work if ranges are equal, e.g. [1:1] and [1:1] ? –  Mar 02 '17 at 11:21
  • 23
    this would be a better answer if you explained the reasoning a bit more – shoosh Mar 03 '18 at 23:13
  • 2
    @Vineet Deoraj - Why do you think it doesn't work? x1 = 1, y1 = 1, x2 = 1, y2 = 1, so x1 <= y2 && y1 <= x2 is true, thus, there is an overlap. – dcp Jun 19 '18 at 13:34
  • 8
    Explanation is here: https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap/325964#325964 – Alex Sep 05 '18 at 19:16
  • This answer could be expanded to include _number_ or half open intervals. – Salman A Feb 08 '19 at 10:34
  • NB the ranges can be contiguous in this case. [x:x2] and [x2:y2]. Salman's question of half open intervals is a good one. – user96265 Jul 11 '19 at 17:41
  • By what rule is the conclusion (x1 <= y2 && y1 <= x2) derived from the two premises (x1 <= C <= x2, y1 <= C <= y2)? – Marius Amado-Alves Dec 20 '21 at 11:51
  • How to check whenever more than two sets of ranges has overlaping in such efficient way? – be-1 Oct 13 '22 at 09:23
193

Given two ranges [x1,x2], [y1,y2]

def is_overlapping(x1,x2,y1,y2):
    return max(x1,y1) <= min(x2,y2)
KFL
  • 17,162
  • 17
  • 65
  • 89
  • 4
    @uyuyuy99 - only not so efficient, because when this check is being done many times per second, calling function is something you would like to avoid, and do as much math yourself, keep it to the basic – vsync Mar 30 '16 at 14:02
  • 18
    @vsync Modern browsers will inline & optimize functions like Math.max, there should be no noticeable impact on performance. – Ashton Six Sep 15 '16 at 01:35
  • 1
    @AshtonWar - interesting. do you have an article explaining what gets inlined and what's not? – vsync Sep 15 '16 at 08:07
  • @vsync No, but I'm sure you can find the information yourself – Ashton Six Sep 15 '16 at 12:02
  • 24
    In addition, note that `min(x2,y2) - max(x1,y1)` provides the amount of overlap in case you need that. – user1556435 Nov 07 '18 at 13:14
  • Also, you can get the intersection with `start = max(x1,y1); end = start + min(x2,y2) - max(x1,y1)`. -Thanks to user1556435 for pointing out the amount of overlap. – Rahat Zaman May 23 '20 at 14:24
96

This can easily warp a normal human brain, so I've found a visual approach to be easier to understand:

overlap madness

le Explanation

If two ranges are "too fat" to fit in a slot that is exactly the sum of the width of both, then they overlap.

For ranges [a1, a2] and [b1, b2] this would be:

/**
 * 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!
}
FloatingRock
  • 6,741
  • 6
  • 42
  • 75
  • 3
    There are more cases than depicted in your pictures. E.g., what if w2 starts before w1 and ends after w1? – WilliamKF Aug 19 '14 at 03:34
  • 9
    @WilliamKF the logic stands true – FloatingRock Sep 27 '15 at 11:41
  • 2
    Agreed, but I think it might help to provide a third picture. – WilliamKF Sep 27 '15 at 20:17
  • 3
    @WilliamKF then you need a lot of more images there are 16 different combinations that 2 ranges can be placed in... – Peter Jan 21 '16 at 15:13
  • 1
    Wikipedia offers a similar argument, without max/min calculations: m1 = (x1 + y1), m2 = (x2 + y2), d1 = (y1 - x1), d2=(y2-x2) overlap = abs(m2 - m1) < d1 + d2 – Román Mar 21 '16 at 22:00
  • @Román: Calculating the abs(m2-m1) is the same as finding the max(m2,m1). If m2>m1 then abs(m2-m1)=m2-m1 else abs(m2-m1)=m1-m2. Which boils down to the same thing as above. – SashaZd Jan 09 '17 at 23:44
  • 1
    @ShashaZd: Thanks for stating the obvious. I was just showing another equivalent way, easily findable in the open Internet. I didn't mean to compare them. – Román Jan 11 '17 at 20:50
  • Will this logic hold up for more than 2 ranges? – mga911 Mar 29 '17 at 16:29
  • 1
    @mga911 no, it won't. Though you could recursively break the problem down into pairs (and use this method), there are more efficient ways to tackle it. – FloatingRock Mar 29 '17 at 16:31
  • 5
    Be careful if you use this method, because the sum `a2 - a1 + b2 - b1` can overflow. To fix it, rearrange the formula to `max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1`, which simplifies to `max(a1, b1) < min(a2, b2)`, saving some arithmetic and avoiding any possible overflows (this is AXE-Labs's answer below). In the special case where you know `b2-b1=a2-a1`, another useful rearrangement of FloatingRock's formula is `max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1`, which becomes `abs(b1-a1) < a2 - a1`. – Paolo Bonzini Mar 01 '18 at 12:23
  • @PaoloBonzini: For future googlers -- using floats, subtraction can underflow. For full correctness in all edge-cases, you should avoid arithmetic and use purely conditionals instead _(ie. use the top answer, after determining which of a1/a2 is larger in the 1% of cases where you don't already know)_ – BlueRaja - Danny Pflughoeft Nov 21 '18 at 20:05
63

Great answer from Simon, but for me it was easier to think about reverse case.

When do 2 ranges not overlap? They don't overlap when one of them starts after the other one ends:

dont_overlap = x2 < y1 || x1 > y2

Now it easy to express when they do overlap:

overlap = !dont_overlap = !(x2 < y1 || x1 > y2) = (x2 >= y1 && x1 <= y2)
Community
  • 1
  • 1
Konstantin Milyutin
  • 11,946
  • 11
  • 59
  • 85
  • 2
    To me, easier to understand expression is : x2 < y1 || y2 < x1 // where I use 'less than' instead of "greater than". – Park JongBum Oct 21 '19 at 00:08
33

Subtracting the Minimum of the ends of the ranges from the Maximum of the beginning seems to do the trick. If the result is less than or equal to zero, we have an overlap. This visualizes it well:

enter image description here

AXE Labs
  • 4,051
  • 4
  • 29
  • 29
9
return x2 >= y1 && x1 <= y2;

Why this works:
The only time the ranges DON'T overlap is when the end of one range is before the beginning of the other. So we want !(x2 < y1 || x1 > y2) which is equivalent to the above.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
9

I suppose the question was about the fastest, not the shortest code. The fastest version have to avoid branches, so we can write something like this:

for simple case:

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));
};

or, for this case:

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);
};
ruslik
  • 14,714
  • 1
  • 39
  • 40
  • 11
    Have faith in your compiler. The expression `x1 <= y2 && y1 <= x2` [doesn't have any branches in it](https://godbolt.org/g/foj6kr) either, assuming a reasonably competent compiler and CPU architecture (even in 2010). In fact, on x86, the generated code is basically identical for the simple expression vs. the code in this answer. – Søren Løvborg Nov 13 '17 at 11:40
5

If you were dealing with, given two ranges [x1:x2] and [y1:y2], natural / anti-natural order ranges at the same time where:

  • natural order: x1 <= x2 && y1 <= y2 or
  • anti-natural order: x1 >= x2 && y1 >= y2

then you may want to use this to check:

they are overlapped <=> (y2 - x1) * (x2 - y1) >= 0

where only four operations are involved:

  • two subtractions
  • one multiplication
  • one comparison
Yankuan Zhang
  • 71
  • 1
  • 6
1

If someone is looking for a one-liner which calculates the actual overlap:

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

If you want a couple fewer operations, but a couple more variables:

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
Victor.dMdB
  • 999
  • 2
  • 11
  • 29
1

Think in the inverse way: how to make the 2 ranges not overlap? Given [x1, x2], then [y1, y2] should be outside [x1, x2], i.e., y1 < y2 < x1 or x2 < y1 < y2 which is equivalent to y2 < x1 or x2 < y1.

Therefore, the condition to make the 2 ranges overlap: not(y2 < x1 or x2 < y1), which is equivalent to y2 >= x1 and x2 >= y1 (same with the accepted answer by Simon).

Duke
  • 1,332
  • 12
  • 12
1

Given: [x1,x2] [y1,y2] then x1 <= y2 || x2 >= y1 would work always. as

      x1 ... x2
y1 .... y2

if x1 > y2 then they do not overlap or

x1 ... x2
    y1 ... y2

if x2 < y1 they do not overlap.

0

You have the most efficient representation already - it's the bare minimum that needs to be checked unless you know for sure that x1 < x2 etc, then use the solutions others have provided.

You should probably note that some compilers will actually optimise this for you - by returning as soon as any of those 4 expressions return true. If one returns true, so will the end result - so the other checks can just be skipped.

Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 2
    All compilers will. All (to my knowledge) currently-used languages with C-style syntax (C, C++, C#, Java, etc.) employ short-circuited boolean operators and it is part of the various standards that govern those languages. If the result of the lefthand value is sufficient to determine the result of the operation, the righthand value is not evaluated. – Jonathan Grynspan Jul 16 '10 at 23:46
  • 1
    Mark H -- the compiler will skip over the second clause if it can: so if you have a function that says: foo(int c) { int i=0; if (c < 3 || ++i == argc) printf("Inside\n"); printf("i is %d\n", i); Foo(2) will print: Inside i is 0 and Foo(4) will print: i is 1 (tested on gcc 4.4.3, but I've relied on this behavior for some ugly code in icc as well) – J Teller Jul 17 '10 at 00:02
0

Nothing new. Just more readable.

def overlap(event_1, event_2):

    start_time_1 = event_1[0]
    end_time_1 = event_1[1]

    start_time_2 = event_2[0]
    end_time_2 = event_2[1]

    start_late = max(start_time_1, start_time_2)
    end_early = min(end_time_1, end_time_2)


    # The event that starts late should only be after the event ending early.
    if start_late > end_early:
        print("Absoloutly No overlap!")
    else:
        print("Events do overlap!")
Codeformer
  • 2,060
  • 9
  • 28
  • 46
0

I believe the min(upper(A),upper(B))>=max(lower(A),lower(B)) will be a great solution not only for its simplicity but also for its extendability beyond two ranges.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 03 '23 at 16:14
-1

My case is different. i want check two time ranges overlap. there should not be a unit time overlap. here is Go implementation.

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

Test cases

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")
    }

you can see there is XOR pattern in boundary comparison

Ajeet47
  • 1,849
  • 3
  • 14
  • 18
-1

Overlap (X, Y) := if (X1 <= Y1) then (Y1 <= X2) else (X1 <= Y2).

PROOF:

Consider the case when X precedes, or is left aligned with, Y, i.e., X1 <= Y1. Then either Y starts inside, or at the end of, X, i.e. Y1 <= X2; or else Y is away from X. The first condition is overlap; the second, not.

In the complementary case when Y precedes X, the same logic applies to the swapped entities.

So,

Overlap (X, Y) := if (X1 <= Y1) then (Y1 <= X2) else Overlap (Y, X).

But this does not seem quite right. On the recursive call, the first test is redundant, as we already know the relative position of the entities from the first test on the first call. So, we really only need to test for the second condition, which, upon swapping, is (X1 <= Y2). So,

Overlap (X, Y) := if (X1 <= Y1) then (Y1 <= X2) else (X1 <= Y2).

QED.

Implementation in Ada:

   type Range_T is array (1 .. 2) of Integer;

   function Overlap (X, Y: Range_T) return Boolean is
     (if X(1) <= Y(1) then Y(1) <= X(2) else X(1) <= Y(2));

Test program:

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is

   type Range_T is array (1 .. 2) of Integer;

   function Overlap (X, Y: Range_T) return Boolean is
     (if X(1) <= Y(1) then Y(1) <= X(2) else X(1) <= Y(2));

   function Img (X: Range_T) return String is
     (" [" & X(1)'Img & X(2)'Img & " ] ");

   procedure Test (X, Y: Range_T; Expect: Boolean) is
      B: Boolean := Overlap (X, Y);
   begin
      Put_Line
        (Img (X) & " and " & Img (Y) &
         (if B then " overlap .......... "
               else " do not overlap ... ") &
         (if B = Expect then "PASS" else "FAIL"));
   end;
         
begin
   Test ( (1, 2), (2, 3), True);  --  chained
   Test ( (2, 3), (1, 2), True);

   Test ( (4, 9), (5, 7), True);  --  inside
   Test ( (5, 7), (4, 9), True);

   Test ( (1, 5), (3, 7), True);  --  proper overlap
   Test ( (3, 7), (1, 5), True);

   Test ( (1, 2), (3, 4), False);  -- back to back
   Test ( (3, 4), (1, 2), False);

   Test ( (1, 2), (5, 7), False);  -- disjoint
   Test ( (5, 7), (1, 2), False);
end;

Output of above program:

 [ 1 2 ]  and  [ 2 3 ]  overlap .......... PASS
 [ 2 3 ]  and  [ 1 2 ]  overlap .......... PASS
 [ 4 9 ]  and  [ 5 7 ]  overlap .......... PASS
 [ 5 7 ]  and  [ 4 9 ]  overlap .......... PASS
 [ 1 5 ]  and  [ 3 7 ]  overlap .......... PASS
 [ 3 7 ]  and  [ 1 5 ]  overlap .......... PASS
 [ 1 2 ]  and  [ 3 4 ]  do not overlap ... PASS
 [ 3 4 ]  and  [ 1 2 ]  do not overlap ... PASS
 [ 1 2 ]  and  [ 5 7 ]  do not overlap ... PASS
 [ 5 7 ]  and  [ 1 2 ]  do not overlap ... PASS
Marius Amado-Alves
  • 1,365
  • 2
  • 8
  • 3
-14

Here's my version:

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;

Unless you're running some high-performance range-checker on billions of widely-spaced integers, our versions should perform similarly. My point is, this is micro-optimization.

Haywood Jablomey
  • 1,255
  • 2
  • 9
  • 3
  • 1
    I think you've gone over the specification here. It's assumed that x1 to x2 is ascending/decending (either way, it's sorted) - there's no need for a loop, you only need to check the head and tail elements. I do prefer the min/max solution though - simply because it's easier to read when you come back to the code later. – Mark H Jul 16 '10 at 23:42
  • 14
    -1: this is not micro-optimisation; this is choosing an appropriate algorithm. Your algorithm is O(n) when there is a simple O(1) choice. – Simon Nickerson Jul 18 '10 at 17:51
  • This is what happens when "premature optimization is the root of all evil" becomes an inviolable religious tenet for the inept instead of a half-serious remark on some occasional pattern of behaviour. – rghome Apr 09 '19 at 09:50