Quelle est la façon la plus efficace de tester le chevauchement de deux plages entières?

251

Étant donné deux plages entières inclusives [x1: x2] et [y1: y2], où x1 ≤ x2 et y1 ≤ y2, quelle est la façon la plus efficace de tester s'il y a un chevauchement des deux plages?

Une implémentation simple est la suivante:

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

Mais je m'attends à ce qu'il existe des moyens plus efficaces de calculer cela.

Quelle méthode serait la plus efficace en termes de moins d'opérations.

WilliamKF
la source
Pourrait être intéressant pour certains - stackoverflow.com/q/17138760/104380
vsync

Réponses:

454

Que signifie le chevauchement des plages? Cela signifie qu'il existe un certain nombre C qui est dans les deux gammes, à savoir

x1 <= C <= x2

et

y1 <= C <= y2

Maintenant, si nous sommes autorisés à supposer que les plages sont bien formées (de sorte que x1 <= x2 et y1 <= y2), il suffit de tester

x1 <= y2 && y1 <= x2
Simon Nickerson
la source
1
Je crois que ça devrait l'être x1 <= y2 && y1 >= x2, non?
David Beck
8
@DavidBeck: non, si y1> x2 alors les plages ne se chevauchent certainement pas (par exemple, considérons [1: 2] et [3: 4]: y1 = 3 et x2 = 2, donc y1> x2, mais il n'y a pas de chevauchement) .
Simon Nickerson
8
ce serait une meilleure réponse si vous
expliquiez
2
@Vineet Deoraj - Pourquoi pensez-vous que cela ne fonctionne pas? x1 = 1, y1 = 1, x2 = 1, y2 = 1, donc x1 <= y2 && y1 <= x2 est vrai, donc il y a un chevauchement.
dcp
2
L'explication est ici: stackoverflow.com/questions/325933/…
Alex
138

Étant donné deux plages [x1, x2], [y1, y2]

def is_overlapping(x1,x2,y1,y2):
    return max(x1,y1) <= min(x2,y2)
KFL
la source
4
@ uyuyuy99 - seulement pas si efficace, car lorsque cette vérification est effectuée plusieurs fois par seconde, la fonction d'appel est quelque chose que vous souhaitez éviter, et faites autant de calculs vous-même, restez à la base
vsync
7
@vsync Les navigateurs modernes incorporeront et optimiseront des fonctions comme Math.max, il ne devrait y avoir aucun impact notable sur les performances.
Ashton Six
1
@AshtonWar - intéressant. avez-vous un article expliquant ce qui est intégré et ce qui ne l'est pas?
vsync
@vsync Non, mais je suis sûr que vous pouvez trouver les informations vous
Ashton Six
6
De plus, notez que cela min(x2,y2) - max(x1,y1)fournit la quantité de chevauchement au cas où vous en auriez besoin.
user1556435
59

Cela peut facilement déformer un cerveau humain normal, j'ai donc trouvé une approche visuelle plus facile à comprendre:

chevauchement de folie

le Explication

Si deux plages sont «trop grasses» pour tenir dans une fente qui est exactement la somme de la largeur des deux, elles se chevauchent.

Pour les gammes [a1, a2]et [b1, b2]ce serait:

/**
 * 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
la source
3
Il y a plus de cas que ceux représentés sur vos photos. Par exemple, que se passe-t-il si w2 commence avant w1 et se termine après w1?
WilliamKF
7
@WilliamKF la logique est vraie
FloatingRock
2
D'accord, mais je pense que cela pourrait aider à fournir une troisième image.
WilliamKF
3
@WilliamKF alors vous avez besoin de beaucoup plus d'images, il y a 16 combinaisons différentes dans lesquelles 2 plages peuvent être placées ...
Peter
3
Soyez prudent si vous utilisez cette méthode, car la somme a2 - a1 + b2 - b1peut déborder. Pour y remédier, réorganisez la formule en max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1, ce qui simplifie max(a1, b1) < min(a2, b2), en économisant de l'arithmétique et en évitant tout débordement possible (c'est la réponse d'AX-Labs ci-dessous). Dans le cas spécial où vous le savez b2-b1=a2-a1, un autre réarrangement utile de la formule de FloatingRock est max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1, qui devient abs(b1-a1) < a2 - a1.
Paolo Bonzini
44

Excellente réponse de Simon , mais pour moi, il était plus facile de penser au cas inverse.

Quand 2 plages ne se chevauchent-elles pas? Ils ne se chevauchent pas lorsque l'un commence après la fin de l'autre:

dont_overlap = x2 < y1 || x1 > y2

Maintenant, il est facile d'exprimer quand ils se chevauchent:

overlap = !dont_overlap = !(x2 < y1 || x1 > y2) = (x2 >= y1 && x1 <= y2)
damluar
la source
1
Pour moi, l'expression la plus facile à comprendre est: x2 <y1 || y2 <x1 // où j'utilise 'inférieur à' au lieu de "supérieur à".
Park JongBum
25

Soustraire le minimum des extrémités des plages du maximum du début semble faire l'affaire. Si le résultat est inférieur ou égal à zéro, nous avons un chevauchement. Cela le visualise bien:

entrez la description de l'image ici

AX Labs
la source
2
Cela couvre tous les cas
user3290180
10

Je suppose que la question portait sur le code le plus rapide et non le plus court. La version la plus rapide doit éviter les branches, nous pouvons donc écrire quelque chose comme ceci:

pour cas simple:

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

ou, dans ce cas:

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
la source
7
Ayez foi en votre compilateur. L'expression x1 <= y2 && y1 <= x2 n'a pas non plus de branches , en supposant un compilateur et une architecture CPU raisonnablement compétents (même en 2010). En fait, sur x86, le code généré est fondamentalement identique pour l'expression simple par rapport au code dans cette réponse.
Søren Løvborg
4
return x2 >= y1 && x1 <= y2;
BlueRaja - Danny Pflughoeft
la source
ce n'est pas correct. Parce que x1 <= y1 && x2 >= y2 || x1 >= y1 && x2 <= y2devrait également retourner vrai.
Rahat Zaman
4

Si vous aviez affaire à deux gammes [x1:x2]et à des gammes d'ordre [y1:y2]naturel / anti-naturel en même temps où:

  • ordre naturel: x1 <= x2 && y1 <= y2ou
  • ordre anti-naturel: x1 >= x2 && y1 >= y2

alors vous pouvez utiliser ceci pour vérifier:

ils se chevauchent <=> (y2 - x1) * (x2 - y1) >= 0

où seulement quatre opérations sont impliquées:

  • deux soustractions
  • une multiplication
  • une comparaison
Yankuan Zhang
la source
1

Si quelqu'un recherche une doublure qui calcule le chevauchement réel:

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

Si vous voulez quelques opérations de moins, mais quelques variables de plus:

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
la source
1

Pensez à l' inverse : comment faire en sorte que les 2 plages ne se chevauchent pas ? Étant donné [x1, x2], alors [y1, y2]devrait être à l' extérieur [x1, x2] , y1 < y2 < x1 or x2 < y1 < y2ce qui est équivalent à y2 < x1 or x2 < y1.

Par conséquent, la condition pour que les 2 plages se chevauchent:, not(y2 < x1 or x2 < y1)ce qui équivaut à y2 >= x1 and x2 >= y1(identique à la réponse acceptée par Simon).

Duc
la source
Ressemble à ce qu'a répondu @damluar (2 mars 16 à 17:36)
Nakilon
0

Vous avez déjà la représentation la plus efficace - c'est le strict minimum qui doit être vérifié, sauf si vous savez avec certitude que x1 <x2, etc., puis utilisez les solutions que d'autres ont fournies.

Vous devriez probablement noter que certains compilateurs optimiseront réellement cela pour vous - en retournant dès que l'une de ces 4 expressions retournera vrai. Si l'un retourne vrai, le résultat final le sera aussi - de sorte que les autres vérifications peuvent simplement être ignorées.

Mark H
la source
2
Tous les compilateurs le feront. Tous (à ma connaissance) les langages actuellement utilisés avec une syntaxe de style C (C, C ++, C #, Java, etc.) utilisent des opérateurs booléens court-circuités et cela fait partie des différentes normes qui régissent ces langages. Si le résultat de la valeur de gauche est suffisant pour déterminer le résultat de l'opération, la valeur de droite n'est pas évaluée.
Jonathan Grynspan
1
Mark H - le compilateur sautera la deuxième clause s'il le peut: donc si vous avez une fonction qui dit: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Inside \ n"); printf ("i est% d \ n", i); Foo (2) imprimera: à l'intérieur, i est 0 et Foo (4) imprimera: i est 1 (testé sur gcc 4.4.3, mais je me suis également appuyé sur ce comportement pour un code laid dans icc)
J Teller
0

Mon cas est différent. je veux vérifier que deux plages de temps se chevauchent. il ne doit pas y avoir de chevauchement de temps unitaire. voici l'implémentation de Go.

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

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

vous pouvez voir qu'il y a un motif XOR dans la comparaison des limites

Ajeet47
la source
-10

Voici ma 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;

À moins que vous exécutiez un vérificateur de plage hautes performances sur des milliards d'entiers largement espacés, nos versions devraient fonctionner de la même manière. Mon point est, c'est la micro-optimisation.

Haywood Jablomey
la source
Je pense que vous avez dépassé les spécifications ici. On suppose que x1 à x2 est croissant / décroissant (de toute façon, il est trié) - il n'y a pas besoin de boucle, il vous suffit de vérifier les éléments head et tail. Je préfère cependant la solution min / max - simplement parce qu'elle est plus facile à lire lorsque vous revenez au code plus tard.
Mark H
12
-1: ce n'est pas de la micro-optimisation; c'est choisir un algorithme approprié. Votre algorithme est O (n) lorsqu'il existe un simple choix O (1).
Simon Nickerson
C'est ce qui se produit lorsque «l'optimisation prématurée est la racine de tout mal» devient un principe religieux inviolable pour les incompétents au lieu d'une remarque semi-sérieuse sur un modèle de comportement occasionnel.
rghome