Was ist der effizienteste Weg, um zwei ganzzahlige Bereiche auf Überlappung zu testen?


250

Was ist bei zwei inklusive ganzzahligen Bereichen [x1: x2] und [y1: y2], wobei x1 ≤ x2 und y1 ≤ y2, der effizienteste Weg, um zu testen, ob es eine Überlappung der beiden Bereiche gibt?

Eine einfache Implementierung lautet wie folgt:

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

Ich gehe jedoch davon aus, dass es effizientere Möglichkeiten gibt, dies zu berechnen.

Welche Methode wäre im Hinblick auf die wenigsten Operationen am effizientesten?


Antworten:


453

Was bedeutet es, wenn sich die Bereiche überschneiden? Dies bedeutet, dass es eine Zahl C gibt, die in beiden Bereichen liegt, d. H.

x1 <= C <= x2

und

y1 <= C <= y2

Wenn wir nun annehmen dürfen, dass die Bereiche gut geformt sind (so dass x1 <= x2 und y1 <= y2), reicht es aus, zu testen

x1 <= y2 && y1 <= x2

1
Ich glaube es sollte sein x1 <= y2 && y1 >= x2, nein?
David Beck

8
@ DavidBeck: Nein, wenn y1> x2, dann überlappen sich die Bereiche definitiv nicht (z. B. [1: 2] und [3: 4] berücksichtigen: y1 = 3 und x2 = 2, also y1> x2, aber es gibt keine Überlappung) .
Simon Nickerson

8
Dies wäre eine bessere Antwort, wenn Sie die Argumentation etwas genauer erläutern würden
shoosh

2
@ Vineet Deoraj - Warum denkst du, funktioniert es nicht? x1 = 1, y1 = 1, x2 = 1, y2 = 1, also ist x1 <= y2 && y1 <= x2 wahr, daher gibt es eine Überlappung.
DCP


137

Bei zwei Bereichen [x1, x2], [y1, y2]

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

4
@ uyuyuy99 - nur nicht so effizient, denn wenn diese Prüfung viele Male pro Sekunde durchgeführt wird, möchten Sie die aufrufende Funktion vermeiden und so viel wie möglich selbst
rechnen. Behalten

7
@vsync Moderne Browser integrieren und optimieren Funktionen wie Math.max. Es sollte keine spürbaren Auswirkungen auf die Leistung geben.
Ashton Six

1
@AshtonWar - interessant. Haben Sie einen Artikel, in dem erklärt wird, was inline wird und was nicht?
vsync

@vsync Nein, aber ich bin sicher, Sie können die Informationen selbst finden
Ashton Six

6
Beachten Sie außerdem, dass min(x2,y2) - max(x1,y1)das Ausmaß der Überlappung angegeben ist, falls Sie dies benötigen.
user1556435

58

Dies kann ein normales menschliches Gehirn leicht verzerren, daher habe ich einen visuellen Ansatz gefunden, der leichter zu verstehen ist:

Überlappungswahnsinn

le Erklärung

Wenn zwei Bereiche "zu fett" sind , um in einen Schlitz zu passen, der genau die Summe der Breite beider ist, überlappen sie sich.

Für Bereiche [a1, a2]und [b1, b2]dies wäre:

/**
 * 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!
}

2
Es gibt mehr Fälle als auf Ihren Bildern dargestellt. Was ist, wenn w2 vor w1 beginnt und nach w1 endet?
WilliamKF

6
@ WilliamKF die Logik steht wahr
FloatingRock

2
Einverstanden, aber ich denke, es könnte helfen, ein drittes Bild zu liefern.
WilliamKF

3
@WilliamKF dann brauchst du viel mehr Bilder, es gibt 16 verschiedene Kombinationen, in denen 2 Bereiche platziert werden können ...
Peter

3
Seien Sie vorsichtig, wenn Sie diese Methode verwenden, da die Summe a2 - a1 + b2 - b1überlaufen kann. Um dies zu beheben, ordnen Sie die Formel neu an max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1, was sich vereinfacht max(a1, b1) < min(a2, b2), indem Sie einige Arithmetik speichern und mögliche Überläufe vermeiden (dies ist die Antwort von AX-Labs unten). In dem speziellen Fall, in dem Sie wissen b2-b1=a2-a1, ist eine andere nützliche Neuanordnung der FloatingRock-Formel max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1, die wird abs(b1-a1) < a2 - a1.
Paolo Bonzini

44

Tolle Antwort von Simon , aber für mich war es einfacher, über den umgekehrten Fall nachzudenken.

Wann überlappen sich 2 Bereiche nicht? Sie überlappen sich nicht, wenn einer von ihnen beginnt, nachdem der andere endet:

dont_overlap = x2 < y1 || x1 > y2

Jetzt ist es einfach auszudrücken, wenn sie sich überschneiden:

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

1
Für mich ist der Ausdruck leichter zu verstehen: x2 <y1 || y2 <x1 // wobei ich 'kleiner als' anstelle von "größer als" verwende.
Park JongBum

24

Das Subtrahieren des Minimums der Enden der Bereiche vom Maximum des Anfangs scheint den Trick zu tun. Wenn das Ergebnis kleiner oder gleich Null ist, haben wir eine Überlappung. Dies macht es gut sichtbar:

Geben Sie hier die Bildbeschreibung ein


1
Dies deckt alle Fälle ab
user3290180

10

Ich nehme an, die Frage betraf den schnellsten und nicht den kürzesten Code. Die schnellste Version muss Verzweigungen vermeiden, damit wir so etwas schreiben können:

für einen einfachen Fall:

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

oder für diesen Fall:

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
Vertrauen Sie Ihrem Compiler. Der Ausdruck x1 <= y2 && y1 <= x2 enthält auch keine Verzweigungen , vorausgesetzt eine einigermaßen kompetente Compiler- und CPU-Architektur (auch im Jahr 2010). Tatsächlich ist auf x86 der generierte Code für den einfachen Ausdruck im Vergleich zum Code in dieser Antwort grundsätzlich identisch.
Søren Løvborg


4

Wenn Sie sich mit zwei Bereichen [x1:x2]und [y1:y2]natürlichen / anti-natürlichen Ordnungsbereichen gleichzeitig befasst haben, in denen:

  • natürliche Ordnung: x1 <= x2 && y1 <= y2oder
  • anti-natürliche Ordnung: x1 >= x2 && y1 >= y2

Dann möchten Sie vielleicht Folgendes verwenden, um Folgendes zu überprüfen:

Sie überlappen sich <=> (y2 - x1) * (x2 - y1) >= 0

wo nur vier Operationen beteiligt sind:

  • zwei Subtraktionen
  • eine Multiplikation
  • ein Vergleich

1

Wenn jemand nach einem Einzeiler sucht, der die tatsächliche Überlappung berechnet:

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

Wenn Sie ein paar weniger Operationen, aber ein paar mehr Variablen wollen:

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

Denken Sie umgekehrt : Wie lassen sich die beiden Bereiche nicht überlappen ? Gegeben [x1, x2], dann [y1, y2]sollte außerhalb sein [x1, x2] , dh y1 < y2 < x1 or x2 < y1 < y2was äquivalent zu ist y2 < x1 or x2 < y1.

Daher überlappt sich die Bedingung, dass sich die beiden Bereiche überschneiden: not(y2 < x1 or x2 < y1)(entspricht y2 >= x1 and x2 >= y1der akzeptierten Antwort von Simon).


Sieht genauso aus wie die Antwort von @damluar (2. März 16 um 17:36 Uhr)
Nakilon

0

Sie haben bereits die effizienteste Darstellung - es ist das absolute Minimum, das überprüft werden muss, es sei denn, Sie wissen sicher, dass x1 <x2 usw. ist, und verwenden Sie dann die von anderen bereitgestellten Lösungen.

Sie sollten wahrscheinlich beachten, dass einige Compiler dies tatsächlich für Sie optimieren - indem sie zurückkehren, sobald einer dieser 4 Ausdrücke true zurückgibt. Wenn einer true zurückgibt, wird auch das Endergebnis angezeigt, sodass die anderen Überprüfungen einfach übersprungen werden können.


2
Alle Compiler werden. Alle (meines Wissens) derzeit verwendeten Sprachen mit C-Syntax (C, C ++, C #, Java usw.) verwenden kurzgeschlossene boolesche Operatoren und sind Teil der verschiedenen Standards, die diese Sprachen regeln. Wenn das Ergebnis des linken Werts ausreicht, um das Ergebnis der Operation zu bestimmen, wird der rechte Wert nicht ausgewertet.
Jonathan Grynspan

1
Markieren Sie H - der Compiler überspringt die zweite Klausel, wenn dies möglich ist: Wenn Sie also eine Funktion haben, die besagt: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Inside \ n"); printf ("i ist% d \ n", i); Foo (2) wird gedruckt: Innerhalb von i ist 0 und Foo (4) wird gedruckt: i ist 1 (getestet auf gcc 4.4.3, aber ich habe mich auch bei hässlichem Code in icc auf dieses Verhalten verlassen)
J Teller

0

Mein Fall ist anders. Ich möchte überprüfen, ob sich zwei Zeitbereiche überschneiden. Es sollte keine zeitliche Überlappung geben. Hier ist die Go-Implementierung.

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

Testfälle

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

Sie können sehen, dass es im Grenzvergleich ein XOR-Muster gibt


-10

Hier ist meine 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;

Sofern Sie keinen Hochleistungs-Bereichsprüfer für Milliarden von weit auseinander liegenden Ganzzahlen ausführen, sollten unsere Versionen eine ähnliche Leistung erbringen. Mein Punkt ist, das ist Mikrooptimierung.


Ich denke, Sie haben die Spezifikation hier durchgesehen. Es wird davon ausgegangen, dass x1 bis x2 auf- / absteigend sind (so oder so, es ist sortiert) - es ist keine Schleife erforderlich, Sie müssen nur die Kopf- und Schwanzelemente überprüfen. Ich bevorzuge jedoch die Min / Max-Lösung - einfach, weil sie leichter zu lesen ist, wenn Sie später zum Code zurückkehren.
Mark H

12
-1: Dies ist keine Mikrooptimierung. Dies ist die Auswahl eines geeigneten Algorithmus. Ihr Algorithmus ist O (n), wenn es eine einfache O (1) -Wahl gibt.
Simon Nickerson

Dies ist der Fall, wenn "vorzeitige Optimierung die Wurzel allen Übels ist" zu einem unantastbaren religiösen Grundsatz für Unfähige wird, anstatt zu einer halb ernsten Bemerkung über gelegentliche Verhaltensmuster.
rghome
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.