İ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.