Apa cara paling efisien untuk menguji dua rentang bilangan bulat untuk tumpang tindih?

251

Diberikan dua rentang bilangan bulat inklusif [x1: x2] dan [y1: y2], di mana x1 ≤ x2 dan y1 ≤ y2, apa cara paling efisien untuk menguji apakah ada tumpang tindih dari kedua rentang?

Implementasi sederhana adalah sebagai berikut:

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

Tapi saya berharap ada cara yang lebih efisien untuk menghitung ini.

Metode apa yang paling efisien dalam hal operasi paling sedikit.

WilliamKF
sumber
Mungkin terkait dengan beberapa hal - stackoverflow.com/q/17138760/104380
vsync

Jawaban:

454

Apa artinya rentang tumpang tindih? Itu berarti ada beberapa angka C yang ada di kedua rentang, yaitu

x1 <= C <= x2

dan

y1 <= C <= y2

Sekarang, jika kita diizinkan untuk mengasumsikan bahwa rentangnya terbentuk dengan baik (sehingga x1 <= x2 dan y1 <= y2) maka cukup untuk menguji

x1 <= y2 && y1 <= x2
Simon Nickerson
sumber
1
Saya percaya seharusnya begitu x1 <= y2 && y1 >= x2, bukan?
David Beck
8
@ Davidvideck: tidak, jika y1> x2 maka rentang pasti tidak tumpang tindih (mis. Pertimbangkan [1: 2] dan [3: 4]: y1 = 3 dan x2 = 2, jadi y1> x2, tetapi tidak ada tumpang tindih) .
Simon Nickerson
8
ini akan menjadi jawaban yang lebih baik jika Anda menjelaskan alasannya lebih sedikit
shoosh
2
@Vineet Deoraj - Mengapa Anda pikir itu tidak berhasil? x1 = 1, y1 = 1, x2 = 1, y2 = 1, jadi x1 <= y2 && y1 <= x2 benar, dengan demikian, ada tumpang tindih.
dcp
2
Penjelasannya ada di sini: stackoverflow.com/questions/325933/...
Alex
138

Diberi dua rentang [x1, x2], [y1, y2]

def is_overlapping(x1,x2,y1,y2):
    return max(x1,y1) <= min(x2,y2)
KFL
sumber
4
@ uyuyuy99 - hanya saja tidak begitu efisien, karena ketika pemeriksaan ini dilakukan berkali-kali per detik, fungsi panggilan adalah sesuatu yang ingin Anda hindari, dan lakukan sebanyak mungkin perhitungan matematis Anda sendiri, pertahankan ke dasarnya
vsync
7
@vsync Browser modern akan inline & mengoptimalkan fungsi seperti Math.max, seharusnya tidak ada dampak nyata pada kinerja.
Ashton Six
1
@AshtonWar - menarik. apakah Anda memiliki artikel yang menjelaskan apa yang diuraikan dan yang tidak?
vsync
@vsync Tidak, tapi saya yakin Anda dapat menemukan informasinya sendiri
Ashton Six
6
Selain itu, perhatikan bahwa min(x2,y2) - max(x1,y1)memberikan jumlah tumpang tindih jika Anda membutuhkannya.
user1556435
59

Ini dapat dengan mudah melengkungkan otak manusia normal, jadi saya telah menemukan pendekatan visual agar lebih mudah dipahami:

tumpang tindih kegilaan

le Penjelasan

Jika dua rentang "terlalu gemuk" untuk masuk dalam slot yang persis jumlah dari lebar keduanya, maka keduanya tumpang tindih.

Untuk rentang [a1, a2]dan [b1, b2]ini akan menjadi:

/**
 * 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
sumber
3
Ada lebih banyak kasus daripada yang digambarkan dalam gambar Anda. Misalnya, bagaimana jika w2 dimulai sebelum w1 dan berakhir setelah w1?
WilliamKF
7
@WilliamKF logikanya benar
FloatingRock
2
Setuju, tapi saya pikir mungkin membantu memberikan gambar ketiga.
WilliamKF
3
@ WilliamKF maka Anda perlu lebih banyak gambar ada 16 kombinasi berbeda yang dapat ditempatkan 2 rentang ...
Peter
3
Hati-hati jika Anda menggunakan metode ini, karena jumlahnya a2 - a1 + b2 - b1bisa melimpah. Untuk memperbaikinya, atur ulang rumus untuk max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1, yang menyederhanakan max(a1, b1) < min(a2, b2), menyimpan beberapa aritmatika dan menghindari kemungkinan meluap (ini adalah jawaban AX-Labs di bawah). Dalam kasus khusus di mana Anda tahu b2-b1=a2-a1, pengaturan ulang lain yang berguna dari formula FloatingRock adalah max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1, yang menjadi abs(b1-a1) < a2 - a1.
Paolo Bonzini
44

Jawaban bagus dari Simon , tetapi bagi saya lebih mudah untuk memikirkan kasus terbalik.

Kapan 2 rentang tidak tumpang tindih? Mereka tidak tumpang tindih ketika salah satu dari mereka mulai setelah yang lain berakhir:

dont_overlap = x2 < y1 || x1 > y2

Sekarang mudah untuk mengekspresikan ketika mereka tumpang tindih:

overlap = !dont_overlap = !(x2 < y1 || x1 > y2) = (x2 >= y1 && x1 <= y2)
damluar
sumber
1
Bagi saya, ekspresi yang lebih mudah dipahami adalah: x2 <y1 || y2 <x1 // tempat saya menggunakan 'kurang dari' alih-alih "lebih besar dari".
Park JongBum
25

Mengurangi Minimum dari ujung rentang dari Maksimum awal tampaknya melakukan trik. Jika hasilnya kurang dari atau sama dengan nol, kami memiliki tumpang tindih. Ini memvisualisasikannya dengan baik:

masukkan deskripsi gambar di sini

Lab AX
sumber
2
Ini mencakup semua kasus
user3290180
10

Saya kira pertanyaannya adalah tentang kode tercepat, bukan kode terpendek. Versi tercepat harus menghindari cabang, sehingga kita dapat menulis sesuatu seperti ini:

untuk kasus sederhana:

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

atau, untuk kasus ini:

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
sumber
7
Percayalah pada kompiler Anda. Ekspresi x1 <= y2 && y1 <= x2 tidak memiliki cabang di dalamnya , dengan asumsi arsitektur kompiler dan CPU yang cukup kompeten (bahkan pada 2010). Pada kenyataannya, pada x86, kode yang dihasilkan pada dasarnya identik dengan ekspresi sederhana vs. kode dalam jawaban ini.
Søren Løvborg
4
return x2 >= y1 && x1 <= y2;
BlueRaja - Danny Pflughoeft
sumber
ini tidak benar. Karena x1 <= y1 && x2 >= y2 || x1 >= y1 && x2 <= y2juga harus mengembalikan true.
Rahat Zaman
4

Jika Anda berurusan dengan, diberikan dua rentang [x1:x2]dan [y1:y2], kisaran urutan alami / anti-alami pada saat yang sama di mana:

  • tatanan alami: x1 <= x2 && y1 <= y2atau
  • tatanan anti-alami: x1 >= x2 && y1 >= y2

maka Anda mungkin ingin menggunakan ini untuk memeriksa:

mereka tumpang tindih <=> (y2 - x1) * (x2 - y1) >= 0

di mana hanya empat operasi yang terlibat:

  • dua pengurangan
  • satu perkalian
  • satu perbandingan
Yankuan Zhang
sumber
1

Jika seseorang mencari satu-liner yang menghitung tumpang tindih aktual:

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

Jika Anda ingin pasangan lebih sedikit operasi, tetapi pasangan lebih banyak variabel:

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
sumber
1

Pikirkan dengan cara terbalik : bagaimana membuat 2 rentang tidak tumpang tindih ? Diberikan [x1, x2], maka [y1, y2]harus di luar [x1, x2] , yaitu y1 < y2 < x1 or x2 < y1 < y2yang setara dengan y2 < x1 or x2 < y1.

Oleh karena itu, kondisi untuk membuat 2 rentang tumpang tindih:, not(y2 < x1 or x2 < y1)yang setara dengan y2 >= x1 and x2 >= y1(sama dengan jawaban yang diterima oleh Simon).

Bangsawan tinggi
sumber
Tampak sama dengan apa yang dijawab @damluar (2 Maret 16 di 17:36)
Nakilon
0

Anda sudah memiliki representasi paling efisien - ini adalah minimum yang perlu diperiksa kecuali Anda tahu pasti bahwa x1 <x2 dll, kemudian gunakan solusi yang telah disediakan orang lain.

Anda mungkin harus memperhatikan bahwa beberapa kompiler benar-benar akan mengoptimalkan ini untuk Anda - dengan mengembalikan segera setelah salah satu dari keempat ekspresi tersebut mengembalikan true. Jika satu mengembalikan true, maka hasil akhirnya - jadi yang lain hanya bisa dilewati.

Markus H
sumber
2
Semua kompiler akan melakukannya. Semua (setahu saya) bahasa yang saat ini digunakan dengan sintaks gaya-C (C, C ++, C #, Java, dll.) Menggunakan operator boolean hubung singkat dan merupakan bagian dari berbagai standar yang mengatur bahasa tersebut. Jika hasil nilai kiri cukup untuk menentukan hasil operasi, nilai sebelah kanan tidak dievaluasi.
Jonathan Grynspan
1
Tandai H - kompiler akan melewati klausa kedua jika dapat: jadi jika Anda memiliki fungsi yang mengatakan: foo (int c) {int i = 0; if (c <3 || ++ i == argc) printf ("Inside \ n"); printf ("i is% d \ n", i); Foo (2) akan mencetak: Di dalam saya adalah 0 dan Foo (4) akan mencetak: saya adalah 1 (diuji pada gcc 4.4.3, tapi saya mengandalkan perilaku ini untuk beberapa kode jelek di icc juga)
J Teller
0

Kasus saya berbeda. saya ingin memeriksa dua rentang waktu yang tumpang tindih. seharusnya tidak ada tumpang tindih satuan waktu. di sini adalah implementasi Go.

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

Uji kasus

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

Anda dapat melihat ada pola XOR dalam perbandingan batas

Ajeet47
sumber
-10

Ini versi saya:

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;

Kecuali jika Anda menjalankan beberapa pemeriksa rentang berkinerja tinggi pada milyaran bilangan bulat yang luas, versi kami akan melakukan hal yang sama. Maksud saya adalah, ini adalah optimasi mikro.

Haywood Jablomey
sumber
Saya pikir Anda sudah membahas spesifikasinya di sini. Diasumsikan bahwa x1 ke x2 naik / turun (baik, itu diurutkan) - tidak perlu untuk loop, Anda hanya perlu memeriksa elemen kepala dan ekor. Saya lebih suka solusi min / max - hanya karena lebih mudah dibaca ketika Anda kembali ke kode nanti.
Mark H
12
-1: ini bukan optimasi mikro; ini memilih algoritma yang sesuai. Algoritme Anda adalah O (n) ketika ada pilihan O (1) sederhana.
Simon Nickerson
Inilah yang terjadi ketika "optimalisasi prematur adalah akar dari semua kejahatan" menjadi prinsip agama yang tidak dapat diganggu gugat bagi orang yang tidak cakap alih-alih komentar setengah serius pada beberapa pola perilaku yang kadang-kadang terjadi.
rghome