2つの整数の範囲の重複をテストする最も効率的な方法は何ですか?


250

2つの包括的な整数範囲[x1:x2]と[y1:y2]が与えられた場合、x1≤x2とy1≤y2の場合、2つの範囲が重複しているかどうかをテストする最も効率的な方法は何ですか?

簡単な実装は次のとおりです。

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/17138760/104380
VSYNC

回答:


453

範囲が重なるとはどういう意味ですか?これは、両方の範囲にある数の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 2013年

8
あなたはもう少し推論説明している場合、これは良い答えだろう
shoosh

2
@Vineet Deoraj-なぜ動かないと思いますか?x1 = 1、y1 = 1、x2 = 1、y2 = 1であるため、x1 <= y2 && y1 <= x2はtrueであり、重複があります。
dcp


138

2つの範囲[x1、x2]、[y1、y2]

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

4
@ uyuyuy99 -だけなので効率的ではありません、このチェックが機能を呼び出すと、あなたは避けたいものです、1秒間に何度も行われているときので、そして多くの数学として自分で行う、基本的にそれを維持
VSYNC

7
@vsync最新のブラウザはMath.maxなどの関数をインライン化および最適化します。パフォーマンスに目立った影響はありません。
アシュトンシックス

1
@AshtonWar-興味深い。インライン化されるものとインライン化されないものを説明する記事はありますか?
vsync

@vsyncいいえ。ただし、自分で情報を見つけられると確信しています
Ashton Six

6
さらに、min(x2,y2) - max(x1,y1)必要な場合に備えて、がオーバーラップの量を提供することに注意してください。
user1556435 2018年

58

これは通常の人間の脳を簡単に歪める可能性があるため、視覚的なアプローチの方が理解しやすいことがわかりました。

狂気を重ねる

le説明

2つの範囲が「太すぎる」ため、両方の幅の合計であるスロットに正確に収まらない場合、範囲は重複します。

範囲の場合[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!
}

2
あなたの写真に描かれているよりも多くのケースがあります。たとえば、w2がw1の前に始まり、w1の後に終わるとどうなりますか?
WilliamKF 2014

6
@WilliamKFの論理は正しい
FloatingRock

2
同意しますが、3つ目の画像を提供すると役立つと思います。
WilliamKF 2015

3
@WilliamKFは、あなたは2つの範囲が中に置くことができることを16個の異なる組み合わせがあり、より多くの画像を必要とする...
ピーター

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-a1max(a2, b2) - min(a1, b1) - (b2 - b1) < a2-a1、FloatingRockの式のもう1つの有用な再​​配置は、になりabs(b1-a1) < a2 - a1ます。
Paolo Bonzini、2018年

44

Simonからの素晴らしい回答ですが、私にとっては、逆のケースについて考える方が簡単でした。

2つの範囲が重ならないのはいつですか?片方がもう片方が終わった後にそれらが始まるとき、それらは重なりません:

dont_overlap = x2 < y1 || x1 > y2

これらが重なる場合は簡単に表現できます。

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

1
私にとって、わかりやすい表現は次のとおりです。x2 <y1 || y2 <x1 //「より大きい」の代わりに「より小さい」を使用します。
Park JongBum

24

最初の最大値から範囲の最小値を減算することは、トリックを行うようです。結果がゼロ以下の場合、重複があります。これはそれをうまく視覚化します:

ここに画像の説明を入力してください


1
これはすべてのケースをカバー
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
コンパイラを信頼してください。(2010年でさえ)適度に有能なコンパイラーとCPUアーキテクチャーを想定すると、式にx1 <= y2 && y1 <= x2 はブランチもありません。実際、x86では、生成されたコードは、単純な式とこの回答のコードでは基本的に同じです。
セーレンLøvborg


4

あなたが扱っていた場合、2つの範囲[x1:x2][y1:y2]、自然/反自然順序範囲が同時に与えられた場合:

  • 自然順:x1 <= x2 && y1 <= y2または
  • 反自然な秩序: x1 >= x2 && y1 >= y2

次に、これを使用して確認することができます。

それらは重複しています<=> (y2 - x1) * (x2 - y1) >= 0

4つの操作のみが関係する場合:

  • 2つの減算
  • 1つの乗算
  • 1つの比較

1

実際のオーバーラップを計算するワンライナーを誰かが探している場合:

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

数少ない操作が必要だが、変数が2つ多い場合:

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 < y2y2 < x1 or x2 < y1

したがって、2つの範囲をオーバーラップさせるための条件:はnot(y2 < x1 or x2 < y1)y2 >= x1 and x2 >= y1(Simonが承認した回答と同じ)と同等です。


@damluarが回答したものと同じように見えます(16年3月2日17:36)
Nakilon

0

あなたはすでに最も効率的な表現を持っています-x1 <x2などであることを確実に知らない限りチェックする必要のある最低限のものであり、他の人が提供したソリューションを使用してください。

これらの4つの式のいずれかがtrueを返すとすぐに戻ることによって、一部のコンパイラーが実際にこれを最適化することに注意する必要があります。1つがtrueを返す場合、最終結果もそうなります-他のチェックはスキップすることができます。


2
すべてのコンパイラはそうします。Cスタイルの構文(C、C ++、C#、Javaなど)で現在使用されているすべての言語(私の知る限り)では、短絡ブール演算子を使用しており、これらの言語を管理するさまざまな標準の一部です。左側の値の結果が演算の結果を決定するのに十分である場合、右側の値は評価されません。
ジョナサングリンスパン

1
マークH-可能な場合、コンパイラーは2番目の節をスキップします。つまり、次のような関数がある場合:foo(int c){int i = 0; if(c <3 || ++ i == argc)printf( "Inside \ n"); printf( "i is%d \ n"、i); Foo(2)は印刷します:Inside i is 0およびFoo(4)は印刷します:i is 1(gcc 4.4.3でテスト済みですが、iccの一部の醜いコードでもこの動作に依存しています)
J Teller

0

私の場合は違います。2つの時間範囲が重複していることを確認します。単位時間の重複があってはなりません。これが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が昇順/降順であると想定されています(どちらの方法でも、ソートされています)。ループの必要はなく、ヘッドとテールの要素を確認するだけで済みます。ただし、後でコードに戻ったときに読みやすいという理由だけで、最小/最大のソリューションを好みます。
Mark H

12
-1:これはミクロ最適化ではありません。これは適切なアルゴリズムを選択しています。単純なO(1)の選択がある場合、アルゴリズムはO(n)です。
Simon Nickerson 2010

これは、「時期尚早な最適化がすべての悪の根源である」が、時々の行動パターンに関する半真面目な発言の代わりに、不得意な人にとって不可侵の宗教的信条になったときに起こることです。
rghome
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.