İki tamsayı aralığını çakışma için test etmenin en etkili yolu nedir?


252

İki x1: x2] ve [y1: y2] olmak üzere iki kapsayıcı tamsayı aralığı verildiğinde, x1 1 x2 ve y1 ≤ y2 olduğunda, iki aralığın çakışmasının olup olmadığını test etmenin en etkili yolu nedir?

Basit bir uygulama aşağıdaki gibidir:

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

Ama bunu hesaplamanın daha etkili yolları olduğunu umuyorum.

En az operasyon açısından en etkili yöntem hangisidir?


Bazıları için ilginç olabilir - stackoverflow.com/q/17138760/104380
vsync

Yanıtlar:


454

Aralıkların çakışması ne anlama geliyor? Yani her iki aralıkta da C sayısı vardır, yani

x1 <= C <= x2

ve

y1 <= C <= y2

Şimdi, aralıkların iyi oluşturulduğunu varsayabilirsek (böylece x1 <= x2 ve y1 <= y2), test etmek yeterlidir

x1 <= y2 && y1 <= x2

1
Olması gerektiğine inanıyorum x1 <= y2 && y1 >= x2, değil mi?
David Beck

8
@DavidBeck: hayır, y1> x2 ise aralıklar kesinlikle üst üste gelmez (örneğin [1: 2] ve [3: 4]: y1 = 3 ve x2 = 2, yani y1> x2, ancak üst üste binme yok) .
Simon Nickerson

8
akıl yürütmeyi biraz daha
açıklarsanız

2
@Vineet Deoraj - Neden işe yaramadığını düşünüyorsun? x1 = 1, y1 = 1, x2 = 1, y2 = 1, bu nedenle x1 <= y2 && y1 <= x2 doğrudur, bu nedenle bir çakışma vardır.
dcp


138

İki aralık verildiğinde [x1, x2], [y1, y2]

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

4
@ uyuyuy99 - sadece o kadar verimli değil, çünkü bu kontrol saniyede birçok kez yapıldığında, arama fonksiyonu kaçınmak istediğiniz bir şeydir ve kendiniz kadar matematik yapın, temelde tutun
vsync

7
@vsync Modern tarayıcılar Math.max gibi işlevleri satır içi ve optimize eder, performans üzerinde belirgin bir etkisi olmamalıdır.
Ashton Six

1
@AshtonWar - ilginç. neyin satılacağını ve neyin satılmadığını açıklayan bir makaleniz var mı?
vsync

@vsync Hayır, ancak bilgileri kendiniz bulabileceğinizden eminim
Ashton Six

6
Ayrıca, min(x2,y2) - max(x1,y1)ihtiyacınız olması durumunda çakışma miktarını da sağladığına dikkat edin .
user1556435

59

Bu normal bir insan beynini kolayca çözebilir, bu yüzden anlaşılması daha kolay bir görsel yaklaşım buldum:

çakışma çılgınlığı

le Açıklama

İki aralık, her ikisinin genişliğinin toplamı olan bir yuvaya sığmayacak kadar "çok yağ" ise, üst üste binerler.

Aralık için [a1, a2]ve [b1, b2]bu:

/**
 * 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
Resimlerinizde tasvir edilenden daha fazla vaka var. Örneğin, w2 w1'den önce başlar ve w1'den sonra biterse ne olur?
WilliamKF

7
@WilliamKF mantık doğru duruyor
FloatingRock

2
Kabul etti, ancak bence üçüncü bir resim sunmaya yardımcı olabilir.
WilliamKF

3
@WilliamKF o zaman çok daha fazla resme ihtiyacınız var 2 aralığın yerleştirilebileceği 16 farklı kombinasyon var ...
Peter

3
Bu yöntemi kullanırsanız dikkatli olun, çünkü toplam a2 - a1 + b2 - b1taşabilir. Düzeltmek için, max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1basitleştirerek max(a1, b1) < min(a2, b2), bazı aritmetiği kaydederek ve olası taşmalardan kaçınarak formülü yeniden düzenleyin (bu, AX-Labs'ın aşağıdaki cevabıdır). Bildiğiniz özel durumda b2-b1=a2-a1, FloatingRock'un formülünün başka bir yararlı yeniden düzenlemesi, max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1olur abs(b1-a1) < a2 - a1.
Paolo Bonzini

44

Simon'dan büyük cevap , ama benim için tersini düşünmek daha kolaydı.

2 aralık ne zaman çakışmaz? Biri diğeri bittikten sonra başladığında örtüşmezler:

dont_overlap = x2 < y1 || x1 > y2

Şimdi örtüştüklerinde ifade etmek kolaydır:

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

1
Bana göre, ifade edilmesi daha kolay: x2 <y1 || y2 <x1 // burada "büyüktür" yerine "küçüktür" ifadesini kullanıyorum.
Park JongBum

26

Aralıkların Minimum sayısının başlangıcın Maksimum değerinden çıkarılması hile yapıyor gibi görünüyor. Sonuç sıfıra eşit veya sıfırsa, bir çakışma olur. Bu onu iyi görselleştirir:

resim açıklamasını buraya girin


2
Bu, tüm durumları kapsar
user3290180

10

Sorunun en kısa kodla değil en hızlı olduğunu düşünüyorum. En hızlı sürüm dallardan kaçınmak zorunda, bu yüzden böyle bir şey yazabiliriz:

basit vaka için:

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

veya bu durum için:

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
Derleyicinize güvenin. İfadenin makul bir şekilde yetkin bir derleyici ve CPU mimarisi (2010'da bile) olduğu varsayılarak da x1 <= y2 && y1 <= x2 hiçbir dalı yoktur . Aslında, x86'da, oluşturulan kod basit bir ifade için bu cevaptaki koda karşılık aynıdır.
Søren Løvborg


4

İki aralık [x1:x2]ve [y1:y2]doğal / anti-doğal düzen aralıkları ile aynı anda ilgileniyor olsaydınız :

  • doğal düzen: x1 <= x2 && y1 <= y2veya
  • anti-doğal sipariş: x1 >= x2 && y1 >= y2

bunu kontrol etmek için kullanmak isteyebilirsiniz:

üst üste biniyorlar <=> (y2 - x1) * (x2 - y1) >= 0

sadece dört işlem söz konusudur:

  • iki çıkarma
  • bir çarpma
  • bir karşılaştırma

1

Birisi gerçek örtüşmeyi hesaplayan bir astar arıyorsa:

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

Daha az işlem, ancak birkaç değişken daha istiyorsanız:

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

Ters düşünün : 2 aralığın üst üste gelmemesini nasıl sağlarım ? Verilen [x1, x2], o [y1, y2]zaman dışarıda [x1, x2] , yani y1 < y2 < x1 or x2 < y1 < y2eşdeğer olmalıdır y2 < x1 or x2 < y1.

Bu nedenle, 2 aralığı yapma koşulu çakışır: not(y2 < x1 or x2 < y1)ki bu eşdeğerdir y2 >= x1 and x2 >= y1(Simon tarafından kabul edilen cevapla aynı).


@Damluar'ın yanıtladığı gibi görünüyor (2 Mart 16:36)
Nakilon

0

Şimdiye kadarki en verimli temsilciliğiniz var - x1 <x2 vb. Olduğundan emin değilseniz, o zaman başkalarının sağladığı çözümleri kullanın.

Muhtemelen bazı derleyicilerin bunu sizin için gerçekten optimize edeceğine dikkat etmelisiniz - bu 4 ifadeden herhangi biri doğru olduğunda hemen geri dönerek. Eğer biri doğruysa, sonuç da sonuçlanır - böylece diğer kontroller atlanabilir.


2
Tüm derleyiciler olacak. Şu anda kullanılan ve C stili sözdizimine sahip tüm diller (C, C ++, C #, Java, vb.) Kısa devreli boole işleçleri kullanır ve bu dilleri yöneten çeşitli standartların bir parçasıdır. Sol el değerinin sonucu, işlemin sonucunu belirlemek için yeterliyse, sağ el değeri değerlendirilmez.
Jonathan Grynspan

1
Mark H - derleyici eğer yapabilirse ikinci cümlenin üzerinden atlar: yani bir fonksiyonunuz varsa: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Inside \ n"); printf ("i% d \ n", i); Foo (2) yazdıracak: Inside i 0 ve Foo (4) yazdıracak: i 1 (gcc 4.4.3'te test edildi, ancak ICC'deki çirkin kodlar için de bu davranışa güvendim)
J Teller

0

Benim durumum farklı. iki zaman aralıkları örtüşme kontrol etmek istiyorum. birim zaman çakışması olmamalıdır. İşte Go uygulaması.

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

Test senaryoları

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

sınır karşılaştırmasında XOR deseni olduğunu görebilirsiniz


-10

İşte benim versiyonum:

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;

Milyarlarca geniş aralıklı tamsayı üzerinde yüksek performanslı bir aralık denetleyicisi çalıştırmıyorsanız, sürümlerimiz benzer şekilde çalışmalıdır. Demek istediğim, bu mikro optimizasyon.


Sanırım burada şartnameyi aştınız. X1 ila x2'nin artan / azalan (her iki şekilde de sıralanır) olduğu varsayılır - bir döngüye gerek yoktur, sadece kafa ve kuyruk elemanlarını kontrol etmeniz gerekir. Min / max çözüm olsa tercih ediyorum - sadece daha sonra koda geri geldiğinde okumak daha kolay çünkü.
Mark H

12
-1: bu mikro optimizasyon değildir; bu uygun bir algoritma seçmektir. Basit bir O (1) seçeneği olduğunda algoritmanız O (n) şeklindedir.
Simon Nickerson

Bu, "erken optimizasyon tüm kötülüğün köküdür", bazı ara sıra davranış biçimleri üzerinde yarı ciddi bir açıklama yerine, beceriksiz bir dinsel inanç haline geldiğinde olan şeydir.
rghome
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.