Qual è il modo più efficiente per testare la sovrapposizione di due intervalli interi?


269

Dati due intervalli interi inclusi [x1: x2] e [y1: y2], dove x1 ≤ x2 e y1 ≤ y2, qual è il modo più efficiente per verificare se vi è una sovrapposizione dei due intervalli?

Una semplice implementazione è la seguente:

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

Ma mi aspetto che ci siano modi più efficienti per calcolarlo.

Quale metodo sarebbe il più efficiente in termini di minor numero di operazioni.


Potrebbe essere interessante notare correlati per alcuni - stackoverflow.com/q/17138760/104380
vsync

Risposte:


474

Cosa significa che gli intervalli si sovrappongono? Significa che esiste un numero C che è in entrambi gli intervalli, ad es

x1 <= C <= x2

e

y1 <= C <= y2

Ora, se possiamo supporre che gli intervalli siano ben formati (in modo che x1 <= x2 e y1 <= y2), allora è sufficiente testare

x1 <= y2 && y1 <= x2

2
Credo che dovrebbe essere x1 <= y2 && y1 >= x2, no?
David Beck

8
@DavidBeck: no, se y1> x2 gli intervalli sicuramente non si sovrappongono (ad esempio, considera [1: 2] e [3: 4]: y1 = 3 e x2 = 2, quindi y1> x2, ma non c'è sovrapposizione) .
Simon Nickerson

9
questa sarebbe una risposta migliore se spiegassi un po 'di più il ragionamento
shoosh

2
@Vineet Deoraj - Perché pensi che non funzioni? x1 = 1, y1 = 1, x2 = 1, y2 = 1, quindi x1 <= y2 && y1 <= x2 è vero, quindi c'è una sovrapposizione.
dcp

2
Alex

144

Dati due intervalli [x1, x2], [y1, y2]

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

4
@ uyuyuy99 - solo non così efficiente, perché quando questo controllo viene eseguito molte volte al secondo, la chiamata alla funzione è qualcosa che vorresti evitare, e fai altrettanti calcoli da solo, mantieniti alla base
vsync

8
@vsync I browser moderni incorporeranno e ottimizzeranno funzioni come Math.max, non dovrebbe esserci alcun impatto evidente sulle prestazioni.
Ashton Six

1
@ AshtonWar - interessante. hai un articolo che spiega cosa viene inserito e cosa no?
vsync

@vsync No, ma sono sicuro che puoi trovare le informazioni da solo
Ashton Six,

7
Inoltre, tieni presente che min(x2,y2) - max(x1,y1)fornisce la quantità di sovrapposizione nel caso in cui ne hai bisogno.
user1556435

63

Questo può facilmente deformare un normale cervello umano, quindi ho trovato un approccio visivo più facile da capire:

si sovrappongono alla follia

le Explanation

Se due intervalli sono "troppo grandi" per rientrare in uno slot che è esattamente la somma della larghezza di entrambi, allora si sovrappongono.

Per gli intervalli [a1, a2]e [b1, b2]questo sarebbe:

/**
 * 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
Ci sono più casi di quelli raffigurati nelle tue foto. Ad esempio, cosa succede se w2 inizia prima di w1 e finisce dopo w1?
WilliamKF

7
@WilliamKF la logica è vera
FloatingRock,

2
D'accordo, ma penso che potrebbe aiutare a fornire una terza immagine.
WilliamKF

3
@WilliamKF allora hai bisogno di molte più immagini, ci sono 16 diverse combinazioni in cui 2 intervalli possono essere inseriti ...
Peter

3
Fai attenzione se usi questo metodo, perché la somma a2 - a1 + b2 - b1può traboccare. Per risolverlo, riorganizza la formula in max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1, il che semplifica max(a1, b1) < min(a2, b2), salvando un po 'di aritmetica ed evitando ogni possibile overflow (questa è la risposta di AX-Labs di seguito). Nel caso speciale in cui sai b2-b1=a2-a1, un altro utile riarrangiamento della formula di FloatingRock è max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1, che diventa abs(b1-a1) < a2 - a1.
Paolo Bonzini

50

Ottima risposta da parte di Simon , ma per me è stato più facile pensare al caso inverso.

Quando 2 intervalli non si sovrappongono? Non si sovrappongono quando uno di essi inizia dopo che l'altro finisce:

dont_overlap = x2 < y1 || x1 > y2

Ora è facile esprimere quando si sovrappongono:

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

2
Per me, l'espressione più facile da capire è: x2 <y1 || y2 <x1 // dove uso "minore di" invece di "maggiore di".
Park JongBum

28

Sottraendo il Minimo delle estremità degli intervalli dal Massimo dell'inizio sembra fare il trucco. Se il risultato è minore o uguale a zero, abbiamo una sovrapposizione. Questo lo visualizza bene:

inserisci qui la descrizione dell'immagine


2
Questo copre tutti i casi
user3290180

10

Suppongo che la domanda riguardasse il codice più veloce, non il più breve. La versione più veloce deve evitare i rami, quindi possiamo scrivere qualcosa del genere:

per caso semplice:

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

oppure, in questo caso:

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
Abbi fiducia nel tuo compilatore. L'espressione x1 <= y2 && y1 <= x2 non ha nemmeno rami in essa , presupponendo un compilatore e un'architettura CPU ragionevolmente competenti (anche nel 2010). In effetti, su x86, il codice generato è fondamentalmente identico per l'espressione semplice rispetto al codice in questa risposta.
Søren Løvborg

5

Se hai a che fare con, dati due intervalli [x1:x2]e intervalli di ordine [y1:y2]naturale / anti-naturale allo stesso tempo dove:

  • ordine naturale: x1 <= x2 && y1 <= y2o
  • ordine anti-naturale: x1 >= x2 && y1 >= y2

allora potresti volerlo usare per controllare:

sono sovrapposti <=> (y2 - x1) * (x2 - y1) >= 0

dove sono coinvolte solo quattro operazioni:

  • due sottrazioni
  • una moltiplicazione
  • un confronto


2

Se qualcuno sta cercando un one-liner che calcoli l'effettiva sovrapposizione:

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

Se vuoi un paio di operazioni in meno, ma un paio di variabili in più:

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

Pensa in modo inverso : come fare in modo che i 2 intervalli non si sovrappongano ? Dato [x1, x2], quindi [y1, y2]dovrebbe essere esterno [x1, x2] , vale a dire, y1 < y2 < x1 or x2 < y1 < y2che è equivalente a y2 < x1 or x2 < y1.

Pertanto, la condizione per sovrapporre i 2 intervalli:, not(y2 < x1 or x2 < y1)che è equivalente a y2 >= x1 and x2 >= y1(lo stesso con la risposta accettata da Simon).


Sembra lo stesso di quello che ha risposto @damluar (2 marzo 16 alle 17:36)
Nakilon

0

Hai già la rappresentazione più efficiente: è il minimo indispensabile che deve essere controllato a meno che tu non sappia per certo che x1 <x2 ecc., Quindi usa le soluzioni fornite da altri.

Probabilmente dovresti notare che alcuni compilatori lo ottimizzeranno effettivamente per te, tornando non appena una di queste 4 espressioni restituirà true. Se uno restituisce vero, così sarà il risultato finale, quindi gli altri controlli possono essere semplicemente saltati.


2
Tutti i compilatori lo faranno. Tutti (per quanto ne so) i linguaggi attualmente in uso con sintassi in stile C (C, C ++, C #, Java, ecc.) Impiegano operatori booleani in cortocircuito e fanno parte dei vari standard che governano quei linguaggi. Se il risultato del valore di sinistra è sufficiente per determinare il risultato dell'operazione, il valore di destra non viene valutato.
Jonathan Grynspan

1
Mark H - il compilatore salterà la seconda clausola se può: quindi se hai una funzione che dice: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Inside \ n"); printf ("i è% d \ n", i); Foo (2) stamperà: Inside i è 0 e Foo (4) stamperà: i è 1 (testato su gcc 4.4.3, ma ho fatto affidamento su questo comportamento anche per del codice brutto in icc)
J Teller

0

Il mio caso è diverso. Voglio controllare che due intervalli di tempo si sovrappongano. non dovrebbe esserci una sovrapposizione di tempo unitario. ecco l'implementazione di Go.

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

Casi test

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

puoi vedere che c'è un modello XOR nel confronto dei confini


0

Dato: [x1,x2] [y1,y2] allora x1 <= y2 || x2 >= y1funzionerebbe sempre. come

      x1 ... x2
y1 .... y2

se x1 > y2poi non si sovrappongono o

x1 ... x2
    y1 ... y2

se x2 < y1non si sovrappongono.


-10

Ecco la mia versione:

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;

A meno che tu non stia eseguendo un controllo di gamma ad alte prestazioni su miliardi di interi molto distanziati, le nostre versioni dovrebbero funzionare in modo simile. Il punto è che questa è micro-ottimizzazione.


Penso che tu abbia esaminato le specifiche qui. Si presume che da x1 a x2 sia crescente / decrescente (in entrambi i casi, è ordinato): non è necessario un ciclo, è sufficiente controllare gli elementi testa e coda. Preferisco la soluzione min / max, semplicemente perché è più facile da leggere quando torni al codice più tardi.
Mark H

12
-1: questa non è microottimizzazione; questa è la scelta di un algoritmo appropriato. Il tuo algoritmo è O (n) quando c'è una semplice scelta O (1).
Simon Nickerson

Questo è ciò che accade quando "l'ottimizzazione prematura è la radice di tutti i mali" diventa un principio religioso inviolabile per gli inetti invece di un'osservazione semiseria su qualche modello occasionale di comportamento.
rghome
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.