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.