중복에 대해 두 정수 범위를 테스트하는 가장 효율적인 방법은 무엇입니까?


252

x1 ≤ x2 및 y1 ≤ y2 인 두 개의 포괄적 인 정수 범위 [x1 : x2] 및 [y1 : y2]가 주어지면 두 범위의 겹침이 있는지 테스트하는 가장 효율적인 방법은 무엇입니까?

간단한 구현은 다음과 같습니다.

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

그러나 이것을 계산하는보다 효율적인 방법이있을 것으로 기대합니다.

가장 적은 작업 측면에서 가장 효율적인 방법은 무엇입니까?


일부 관련하여 흥미롭게 관련 될 수 있습니다.- stackoverflow.com
q

답변:


454

범위가 겹치는 것은 무엇을 의미합니까? 즉, 두 범위에있는 숫자 C가 있음을 의미합니다.

x1 <= C <= x2

y1 <= C <= y2

이제 범위가 잘 구성되어 있다고 가정하면 (x1 <= x2 및 y1 <= y2) 테스트하기에 충분합니다.

x1 <= y2 && y1 <= x2

1
그래야한다고 생각 x1 <= y2 && y1 >= x2합니까?
David Beck

8
@DavidBeck : 아니요, y1> x2 인 경우 범위가 겹치지 않습니다 (예 : [1 : 2] 및 [3 : 4] : y1 = 3 및 x2 = 2를 고려하므로 y1> x2이지만 겹치지 않습니다) .
Simon Nickerson

8
추론을 조금 더 설명하면 더 나은 답변이 될 것입니다
shoosh

2
@Vineet Deoraj-왜 작동하지 않는다고 생각하십니까? x1 = 1, y1 = 1, x2 = 1, y2 = 1이므로 x1 <= y2 && y1 <= x2는 true이므로 겹치는 부분이 있습니다.
dcp

2
설명은 다음과 같습니다. stackoverflow.com/questions/325933/…
Alex

138

두 가지 범위 [x1, x2], [y1, y2]

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

4
@ uyuyuy99 - 만 그렇게 효율적이지,이 검사는 함수를 호출하면 기본으로 유지, 당신이 피하기 위해 같은, 그리고 훨씬 수학 자신을 할 것입니다 무언가이다, 초당 여러 번 수행 될 때 때문에
수직 동기화

7
@vsync 최신 브라우저는 Math.max와 같은 기능을 인라인 및 최적화하므로 성능에 눈에 띄는 영향이 없어야합니다.
Ashton Six

1
@AshtonWar-흥미 롭습니다. 무엇이 인라인되고 무엇이 아닌지 설명하는 기사가 있습니까?
vsync

@vsync 아니요,하지만 직접 정보를 찾을 수 있다고 확신합니다
Ashton Six

6
또한 min(x2,y2) - max(x1,y1)필요한 경우 겹치는 정도 를 제공합니다.
user1556435

59

이것은 정상적인 인간의 뇌를 쉽게 뒤틀 수 있기 때문에 이해하기 쉬운 시각적 접근 방식을 찾았습니다.

겹치는 광기

르 설명

두 범위가 "너무 뚱뚱해" 두 슬롯의 너비를 모두 합한 슬롯에 맞으면 겹쳐집니다.

범위의 경우 다음 [a1, a2][b1, b2]같습니다.

/**
 * 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
사진에 묘사 된 것보다 더 많은 경우가 있습니다. 예를 들어, w2가 w1 이전에 시작되고 w1 이후에 종료되면 어떻게됩니까?
WilliamKF

7
@WilliamKF 논리는 사실
FloatingRock

2
동의하지만 세 번째 그림을 제공하는 것이 도움이 될 것 같습니다.
WilliamKF

3
@WilliamKF 그렇다면 더 많은 이미지가 필요합니다. 2 개의 범위를 배치 할 수있는 16 가지 조합이 있습니다.
Peter

3
합계 a2 - a1 + b2 - b1가 오버플로 될 수 있으므로이 방법을 사용하면주의하십시오 . 이 문제를 해결하려면 수식을로 재정렬하면 max(a2, b2) - a2 - b2 < min(a1, b1) - a1 - b1을 단순화하여 max(a1, b1) < min(a2, b2)산술을 저장하고 오버플로를 피할 수 있습니다 (이것은 아래의 AXE-Labs의 답변입니다). 당신이 아는 특별한 경우에 b2-b1=a2-a1, FloatingRock의 공식의 또 다른 유용한 재 배열 max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1abs(b1-a1) < a2 - a1입니다.
Paolo Bonzini

44

Simon의 큰 대답 이지만 저에게는 반대 사례를 생각하는 것이 더 쉬웠습니다.

두 범위가 겹치지 않는 경우는 언제입니까? 그것들 중 하나가 다른 하나가 끝난 후에 시작될 때 겹치지 않습니다.

dont_overlap = x2 < y1 || x1 > y2

이제 겹칠 때 쉽게 표현할 수 있습니다.

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

1
나에게 표현을 이해하기 쉽게 : x2 <y1 || y2 <x1 // 여기서 "보다 큼"대신 '보다 작음'을 사용합니다.
Park JongBum

26

시작의 최대 값에서 범위의 끝의 최소값을 빼면 트릭을 수행하는 것 같습니다. 결과가 0보다 작거나 같으면 겹칩니다. 이것은 그것을 잘 시각화합니다 :

여기에 이미지 설명을 입력하십시오


2
여기에는 모든 사례가 포함됩니다
user3290180

10

질문은 가장 짧은 코드가 아니라 가장 빠른 코드라고 생각합니다. 가장 빠른 버전은 가지를 피해야하므로 다음과 같이 작성할 수 있습니다.

간단한 경우 :

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

또는이 경우 :

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
컴파일러를 믿으십시오. 합리적으로 유능한 컴파일러와 CPU 아키텍처 (2010 년조차도)를 가정하면 이 표현식 x1 <= y2 && y1 <= x2 에는 분기가 없습니다 . 실제로 x86에서 생성 된 코드는 기본적 으로이 답변의 코드와 간단한 표현에 동일합니다.
Søren Løvborg


4

두 가지 범위 [x1:x2][y1:y2]자연 / 반 자연적 순서 범위를 동시에 다루는 경우 :

  • 자연 질서 : x1 <= x2 && y1 <= y2또는
  • 비 자연적 질서 : x1 >= x2 && y1 >= y2

다음을 사용하여 확인할 수 있습니다.

그들은 겹친다 <=> (y2 - x1) * (x2 - y1) >= 0

경우에만 네 개의 작업이 참여하고 있습니다 :

  • 두 빼기
  • 한 번의 곱셈
  • 하나의 비교

1

누군가가 실제 중첩을 계산하는 하나의 라이너를 찾고 있다면 :

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

몇 가지 더 적은 작업을 원하지만 몇 가지 더 많은 변수를 원할 경우 :

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

반대의 방법으로 생각하십시오 : 2 개의 범위를 겹치지 않게 하는 방법 ? 을 감안할 때 [x1, x2], 다음 [y1, y2]해야 외부 [x1, x2] , 즉, y1 < y2 < x1 or x2 < y1 < y2에 해당한다 y2 < x1 or x2 < y1.

따라서 두 범위를 겹치는 조건은 겹치게됩니다. not(y2 < x1 or x2 < y1)이는 y2 >= x1 and x2 >= y1(Simon의 허용 된 대답과 동일)입니다.


@damluar의 답변과 동일하게 보임 (17 년 3 월 2 일 17:36)
Nakilon

0

당신은 이미 가장 효율적인 표현을 가지고 있습니다-x1 <x2 등을 확실히 알지 못한다면 다른 사람들이 제공 한 솔루션을 사용하지 않는 한 확인해야 할 최소값입니다.

아마도 4 개의 표현식 중 하나라도 true를 반환하자마자 반환함으로써 일부 컴파일러는 실제로이를 최적화 할 것입니다. 하나가 true를 반환하면 최종 결과도 마찬가지이므로 다른 검사는 건너 뛸 수 있습니다.


2
모든 컴파일러는 그렇게 할 것입니다. C 스타일 구문 (C, C ++, C #, Java 등)으로 현재 사용되는 모든 언어는 단락 부울 연산자를 사용하며 해당 언어를 제어하는 ​​다양한 표준의 일부입니다. 왼쪽 값의 결과가 연산 결과를 결정하기에 충분하면 오른쪽 값은 평가되지 않습니다.
Jonathan Grynspan

1
Mark H-컴파일러는 다음과 같은 경우에 두 번째 절을 건너 뜁니다. if (c <3 || ++ i == argc) printf ( "Inside \ n"); printf ( "i는 % d \ n", i); Foo (2)가 인쇄됩니다 : 내부 i는 0이고 Foo (4)가 인쇄합니다 : i는 1입니다 (gcc 4.4.3에서 테스트되었지만 icc의 일부 못생긴 코드에 대해서도이 동작에 의존했습니다)
J Teller

0

제 사건은 다릅니다. 두 시간 범위가 겹치는 지 확인하고 싶습니다. 단위 시간 중복이 없어야합니다. 다음은 Go 구현입니다.

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

테스트 사례

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

경계 비교에 XOR 패턴이 있음을 알 수 있습니다


-10

내 버전은 다음과 같습니다.

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;

수십억 개의 넓은 간격의 정수에 대해 고성능 범위 검사기를 실행하지 않는 한, 우리의 버전도 비슷하게 작동해야합니다. 내 요점은 이것이 미세 최적화입니다.


나는 당신이 여기에 사양을 넘어 갔다고 생각합니다. x1에서 x2는 오름차순 / 내림차순으로 가정합니다 (둘 중 어느 쪽이든 정렬 됨)-루프가 필요 없으며 머리와 꼬리 요소 만 확인하면됩니다. 그래도 min / max 솔루션을 선호합니다. 나중에 코드로 돌아올 때 더 쉽게 읽을 수 있기 때문입니다.
Mark H

12
-1 : 이것은 미세 최적화가 아닙니다. 이것은 적절한 알고리즘을 선택하는 것입니다. 간단한 O (1) 선택이있을 때 알고리즘은 O (n)입니다.
Simon Nickerson

이것은 "조기 최적화는 모든 악의 근원"이 때때로 행동의 일부 패턴에 대해 반 심각하게 언급하는 대신에 부적당 한 종교적 교리가 될 때 발생합니다.
rghome
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.