Cách hiệu quả nhất để kiểm tra hai phạm vi số nguyên cho sự chồng chéo là gì?


250

Cho hai phạm vi số nguyên bao gồm [x1: x2] và [y1: y2], trong đó x1 ≤ x2 và y1 y2, cách hiệu quả nhất để kiểm tra xem có sự chồng chéo nào của hai phạm vi không?

Một cách thực hiện đơn giản như sau:

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

Nhưng tôi hy vọng có nhiều cách hiệu quả hơn để tính toán điều này.

Phương pháp nào sẽ hiệu quả nhất về các hoạt động ít nhất.


Có thể liên quan thú vị đối với một số - stackoverflow.com/q/17138760/104380
vsync

Câu trả lời:


453

Nó có nghĩa gì cho các phạm vi chồng chéo? Nó có nghĩa là tồn tại một số số C trong cả hai phạm vi, nghĩa là

x1 <= C <= x2

y1 <= C <= y2

Bây giờ, nếu chúng ta được phép giả định rằng các phạm vi được hình thành tốt (sao cho x1 <= x2 và y1 <= y2) thì đủ để kiểm tra

x1 <= y2 && y1 <= x2

1
Tôi tin rằng nó nên x1 <= y2 && y1 >= x2, không?
David Beck

8
@DavidBeck: không, nếu y1> x2 thì các phạm vi chắc chắn không trùng nhau (ví dụ: xem xét [1: 2] và [3: 4]: y1 = 3 và x2 = 2, vì vậy y1> x2, nhưng không có sự trùng lặp) .
Simon Nickerson

8
đây sẽ là một câu trả lời tốt hơn nếu bạn giải thích lý do nhiều hơn một chút
shoosh

2
@Vineet Deoraj - Tại sao bạn nghĩ rằng nó không hoạt động? x1 = 1, y1 = 1, x2 = 1, y2 = 1, do đó x1 <= y2 && y1 <= x2 là đúng, do đó, có sự trùng lặp.
dcp

2
Giải thích tại đây: stackoverflow.com/questions/325933/ Khăn
Alex

137

Cho hai phạm vi [x1, x2], [y1, y2]

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

4
@ uyuyuy99 - chỉ không hiệu quả lắm, bởi vì khi kiểm tra này được thực hiện nhiều lần trong một giây, chức năng gọi điện là thứ bạn muốn tránh và tự mình làm toán nhiều như vậy, hãy giữ nó ở mức cơ bản
vsync

7
@vsync Các trình duyệt hiện đại sẽ nội tuyến & tối ưu hóa các chức năng như Math.max, sẽ không có tác động đáng chú ý nào đến hiệu suất.
Ashton Sáu

1
@AshtonWar - thú vị. Bạn có một bài viết giải thích những gì được nội tuyến và những gì không?
vsync

@vsync Không, nhưng tôi chắc chắn bạn có thể tự tìm thông tin
Ashton Six

6
Ngoài ra, lưu ý rằng min(x2,y2) - max(x1,y1)cung cấp số lượng chồng lấp trong trường hợp bạn cần điều đó.
dùng1556435

58

Điều này có thể dễ dàng làm cong vênh bộ não người bình thường, vì vậy tôi đã tìm thấy một cách tiếp cận trực quan để dễ hiểu hơn:

chồng chéo điên rồ

giải thích

Nếu hai phạm vi "quá béo" để khớp với một vị trí chính xác bằng tổng chiều rộng của cả hai, thì chúng trùng nhau.

Đối với phạm vi [a1, a2][b1, b2]điều này sẽ là:

/**
 * 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!
}

2
Có nhiều trường hợp hơn mô tả trong hình ảnh của bạn. Ví dụ, nếu w2 bắt đầu trước w1 và kết thúc sau w1 thì sao?
WilliamKF

6
@WilliamKF logic là đúng
FloatingRock

2
Đồng ý, nhưng tôi nghĩ nó có thể giúp cung cấp một bức tranh thứ ba.
WilliamKF

3
@WilliamKF sau đó bạn cần rất nhiều hình ảnh khác, có 16 kết hợp khác nhau mà 2 phạm vi có thể được đặt trong ...
Peter

3
Hãy cẩn thận nếu bạn sử dụng phương pháp này, vì tổng a2 - a1 + b2 - b1có thể tràn. Để khắc phục, hãy sắp xếp lại công thức để max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1đơn giản hóa max(a1, b1) < min(a2, b2), lưu một số số học và tránh mọi trường hợp có thể xảy ra (đây là câu trả lời của AX-Labs bên dưới). Trong trường hợp đặc biệt mà bạn biết b2-b1=a2-a1, một sự sắp xếp lại hữu ích khác của công thức FloatingRock là max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1, sẽ trở thành abs(b1-a1) < a2 - a1.
Paolo Bonzini

44

Câu trả lời tuyệt vời từ Simon , nhưng đối với tôi thì dễ dàng hơn khi nghĩ về trường hợp ngược lại.

Khi nào 2 phạm vi không trùng nhau? Chúng không trùng nhau khi một trong số chúng bắt đầu sau khi một cái khác kết thúc:

dont_overlap = x2 < y1 || x1 > y2

Bây giờ thật dễ dàng để thể hiện khi họ làm chồng chéo:

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

1
Đối với tôi, biểu thức dễ hiểu hơn là: x2 <y1 || y2 <x1 // trong đó tôi sử dụng 'ít hơn' thay vì "lớn hơn".
Park JongBum

24

Trừ tối thiểu các đầu của các phạm vi từ Tối đa của đầu bắt đầu dường như thực hiện thủ thuật. Nếu kết quả nhỏ hơn hoặc bằng 0, chúng ta có sự trùng lặp. Điều này hình dung nó tốt:

nhập mô tả hình ảnh ở đây


1
Điều này bao gồm tất cả các trường hợp
user3290180

10

Tôi cho rằng câu hỏi là về mã nhanh nhất chứ không phải mã ngắn nhất. Phiên bản nhanh nhất phải tránh các nhánh, vì vậy chúng ta có thể viết một cái gì đó như thế này:

cho trường hợp đơn giản:

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

hoặc, đối với trường hợp này:

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
Có niềm tin vào trình biên dịch của bạn. Biểu thức x1 <= y2 && y1 <= x2 không có bất kỳ nhánh nào trong đó , giả sử một trình biên dịch và kiến ​​trúc CPU có thẩm quyền hợp lý (ngay cả trong năm 2010). Trong thực tế, trên x86, mã được tạo về cơ bản là giống hệt nhau cho biểu thức đơn giản so với mã trong câu trả lời này.
Søren Løvborg


4

Nếu bạn đang giao dịch, đưa ra hai phạm vi [x1:x2][y1:y2], phạm vi trật tự tự nhiên / chống tự nhiên cùng một lúc trong đó:

  • trật tự tự nhiên: x1 <= x2 && y1 <= y2hoặc
  • trật tự chống tự nhiên: x1 >= x2 && y1 >= y2

sau đó bạn có thể muốn sử dụng điều này để kiểm tra:

chúng bị chồng chéo <=> (y2 - x1) * (x2 - y1) >= 0

nơi chỉ có bốn hoạt động được tham gia:

  • hai phép trừ
  • một phép nhân
  • một so sánh

1

Nếu ai đó đang tìm kiếm một lớp lót tính toán sự chồng chéo thực tế:

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

Nếu bạn muốn một vài thao tác ít hơn, nhưng một vài biến số hơn:

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

Suy nghĩ theo cách ngược lại : làm thế nào để 2 phạm vi không trùng nhau ? Cho [x1, x2], sau đó [y1, y2]nên ở bên ngoài [x1, x2] , tức là, y1 < y2 < x1 or x2 < y1 < y2tương đương với y2 < x1 or x2 < y1.

Do đó, điều kiện để làm cho 2 phạm vi trùng nhau : not(y2 < x1 or x2 < y1), tương đương với y2 >= x1 and x2 >= y1(cùng với câu trả lời được chấp nhận bởi Simon).


Trông giống như những gì @damluar đã trả lời (2 tháng 2, 16 lúc 17:36)
Nakilon

0

Bạn đã có đại diện hiệu quả nhất - đó là mức tối thiểu cần kiểm tra trừ khi bạn biết chắc rằng x1 <x2, v.v., sau đó sử dụng các giải pháp mà người khác đã cung cấp.

Bạn có thể nên lưu ý rằng một số trình biên dịch sẽ thực sự tối ưu hóa điều này cho bạn - bằng cách quay lại ngay khi bất kỳ 4 biểu thức nào trở về đúng. Nếu một trả về đúng, kết quả cuối cùng cũng vậy - vì vậy các kiểm tra khác có thể được bỏ qua.


2
Tất cả các trình biên dịch sẽ. Tất cả (theo hiểu biết của tôi) các ngôn ngữ hiện đang sử dụng với cú pháp kiểu C (C, C ++, C #, Java, v.v.) sử dụng các toán tử boolean ngắn mạch và nó là một phần của các tiêu chuẩn khác nhau chi phối các ngôn ngữ đó. Nếu kết quả của giá trị lefthand là đủ để xác định kết quả của hoạt động, giá trị tay phải không được đánh giá.
Jonathan Grynspan

1
Đánh dấu H - trình biên dịch sẽ bỏ qua mệnh đề thứ hai nếu nó có thể: vì vậy nếu bạn có một hàm có nội dung: foo (int c) {int i = 0; if (c <3 | | ++ i == argc) printf ("Bên trong \ n"); printf ("i là% d \ n", i); Foo (2) sẽ in: Bên trong i là 0 và Foo (4) sẽ in: i là 1 (đã thử nghiệm trên gcc 4.4.3, nhưng tôi cũng đã dựa vào hành vi này đối với một số mã xấu trong icc)
J Teller

0

Trường hợp của tôi thì khác. tôi muốn kiểm tra hai phạm vi thời gian chồng chéo. không nên có một đơn vị thời gian chồng chéo. Đây là thực hiện Go.

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

Các trường hợp thử nghiệm

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

bạn có thể thấy có mẫu XOR trong so sánh ranh giới


-10

Đây là phiên bản của tôi:

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;

Trừ khi bạn đang chạy một số trình kiểm tra phạm vi hiệu suất cao trên hàng tỷ số nguyên có khoảng cách rộng rãi, các phiên bản của chúng tôi sẽ hoạt động tương tự. Quan điểm của tôi là, đây là tối ưu hóa vi mô.


Tôi nghĩ rằng bạn đã đi qua các đặc điểm kỹ thuật ở đây. Giả sử x1 đến x2 tăng dần / giảm dần (theo cách nào đó, nó đã được sắp xếp) - không cần vòng lặp, bạn chỉ cần kiểm tra các yếu tố đầu và đuôi. Tôi thực sự thích giải pháp tối thiểu / tối đa - đơn giản vì nó dễ đọc hơn khi bạn quay lại mã sau này.
Đánh dấu

12
-1: đây không phải là tối ưu hóa vi mô; đây là chọn một thuật toán thích hợp Thuật toán của bạn là O (n) khi có lựa chọn O (1) đơn giản.
Simon Nickerson

Đây là những gì xảy ra khi "tối ưu hóa sớm là gốc rễ của mọi tội lỗi" trở thành một nguyên lý tôn giáo bất khả xâm phạm đối với những kẻ bất tài thay vì một nhận xét nửa nghiêm túc về một số hành vi thỉnh thoảng.
rghome
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.