垂直方向の可視性問題のための効率的なアルゴリズム


18

1つの問題を考えると、次のタスクを解決する効率的なアルゴリズムを作成する必要があることに気付きました。

問題:辺が軸に平行な辺 2次元の正方形の箱が与えられます。頂上から見ることができます。ただし、水平セグメントもあります。各セグメントには整数座標()および()があり、ポイントおよび(下の写真)。m個のY 0 Y N X 0 X 1 < X 2N X 1Y )、 X 2Y nmy0ynx0x1<x2n(x1,y)(x2,y)

ボックスの上部にある各ユニットセグメントについて、このセグメントを覗いた場合にボックス内をどのくらい深く見ることができるかを知りたいと思います。

正式には、場合、。x{0,,n1}maxi: [x,x+1][x1,i,x2,i]yi

例:次の図のようにn=9およびm=7セグメントがある場合、結果は(5,5,5,3,8,3,7,8,7)です。箱の中にどれだけ深い光が入るかを見てください。

7つのセグメント。 網掛け部分は、光が到達できる領域を示しています

幸いなことに、nmはどちらも非常に小さく、オフラインで計算を行うことができます。

この問題を解決する最も簡単なアルゴリズムはブルートフォースです。各セグメントでアレイ全体を走査し、必要に応じて更新します。ただし、O(mn)はあまり印象的ではありませんO(mn)

大幅な改善は、クエリ中にセグメントの値を最大化し、最終値を読み取ることができるセグメントツリーを使用することです。これ以上は説明しませんが、時間の複雑さはことがわかります。O((m+n)logn)

しかし、私はより速いアルゴリズムを思いつきました:

概要:

  1. 座標の降順にセグメントをソートします(カウントソートのバリエーションを使用した線形時間)。ここで、以前にユニットセグメントがいずれかのセグメントでカバーされていた場合、後続のセグメントは、このユニットセグメントを通過する光線をバインドできなくなります。次に、ボックスの上部から下部へのラインスイープを実行します。yxx

  2. それでは、いくつかの定義を紹介しましょう: -unitセグメント掃引上の仮想水平セグメントである -coordinates整数であり、長さが1の掃引プロセス中に各セグメントでは、いずれであってもよいマークされていないから行く光ビームです(ボックスの上部がこのセグメントに到達するか、マークされているか(反対のケース)。常にマークされていない、ユニットセグメントを考えます。セットも導入しましょう。各セットは、連続の全配列が含まれますマーク以下で(もしあれば)セグメントを-unit 無印xxxx1=nx2=n+1S0={0},S1={1},,Sn={n} x セグメント。

  3. これらのセグメントとセットを効率的に操作できるデータ構造が必要です。最大のユニットセグメントインデックス(マークされていないセグメントのインデックス)を保持するフィールドによって拡張されたfind-union構造を使用しますx

  4. これで、セグメントを効率的に処理できます。ここで、で始まりで終わる番目のセグメントを順番に検討しているとしましょう(「クエリ」と呼びます)。番目のセグメント内に含まれる、マークされていないすべてのユニットセグメントを見つける必要があります(これらは、光ビームが途中で終了するセグメントです)。以下を実行します。まず、クエリ内で最初のマークされていないセグメントを見つけます(が含まれるセットの代表を見つけ、このセットの最大インデックスを取得しますこれは定義によりマークされていないセグメントです)。次に、このインデックスix1x2 xix1xクエリ内にある場合、結果に追加し(このセグメントの結果は)、このインデックスをマークします(とを含むUnionセット)。次に、すべてのマークされていないセグメントが見つかるまでこの手順を繰り返します。つまり、次の検索クエリはインデックスます。yxx+1xx2

各検索共用体操作は2つの場合にのみ行われることに注意してください。セグメントの検討を開始する(これは回発生する)か、ユニットセグメントをマークしただけです(これは回発生する可能性があります)。したがって、全体的な複雑さは(は逆アッカーマン関数です)。不明な点がある場合は、これについて詳しく説明します。時間があれば、写真を追加できるかもしれません。mxnO((n+m)α(n))α

今、私は「壁」に到達しました。私は線形アルゴリズムを思い付くことができませんが、線形アルゴリズムがあるはずです。だから、私は2つの質問があります:

  • 水平セグメントの可視性の問題を解決する線形時間アルゴリズム(つまり、)はありますか?O(n+m)
  • そうでない場合、可視性の問題があるという証拠は何ですか?ω(n+m)

m個のセグメントをどのくらい速くソートしますか?
babou 14

@babou、質問はカウントソートを指定します。質問が言うように、それは線形時間(「カウントソートのバリエーションを使用した線形時間」)で実行されます。
DW

左から右に掃引してみましたか?必要なのは、と両方のステップでとソートして右に歩くことです。したがって、合計。x1x2O(m)O(m)O(m)
invalid_id

@invalid_idはい、試しました。ただし、この場合、スイープラインは、セグメントの先頭に一致する(つまり、セグメントの座標に等しい数をマルチセットに追加する)ときに適切に反応し、セグメントの末尾に一致する(出現を削除する)必要があります-coordinate)、最高のアクティブセグメントを出力します(マルチセットの最大値を出力します)。(償却された)一定の時間でこれを可能にするデータ構造について聞いたことはありません。yy
mnbvmar 14

@mnbvmarは愚かな提案かもしれませんが、サイズ配列については、すべてのセルをスイープして停止します 。evryセルの場合、最大がわかっているので、マトリックスに入力できます。さらに、変数を使用して全体の最大を追跡できます。nO(n)y
invalid_id

回答:


1
  1. 最初に、2つの別々の配列およびラインのおよび座標の両方をソートします。x1x2ABO(m)
  2. また、アクティブなセグメントを追跡するために、補助ビット配列サイズを維持します。n
  3. 左から右へ掃引を開始します。
  4. 以下のための(i=0,i<n,i++)
  5. {
  6. ..if with valuex1=iyc O(1)
  7. .. {
  8. .... find()max
  9. .... store()maxO(1)
  10. ..}
  11. ..if with valuex2=iyc O(1)
  12. .. {
  13. .... find()max
  14. .... store()maxO(1)
  15. ..}
  16. }

find()は、ビットのビット配列を使用して実装できます。これで、に要素を削除または追加するたびに、ビットをそれぞれtrueまたはfalseに設定してこの整数を更新できます。使用するプログラミング言語に応じて2つのオプションがあり、仮定は比較的小さい、つまり少なくとも64ビットまたはこれらの整数の固定量であるよりも小さい:maxnLnlonglongint

  • 一定時間で最下位ビットを取得することは、一部のハードウェアとgccでサポートされています。
  • を整数変換すると、最大値が得られます(直接ではなく、導出できます)。LO(1)

私はそれがために最大値を想定しているので、これはかなりハックです知っ、したがって、、その後の定数として見ることができます...nn


ご覧のとおり、64ビットのx86プロセッサを使用していると仮定すると、のみを処理できます。が数百万のオーダーの場合はどうなりますか?n64n
mnbvmar 14

その後、さらに整数が必要になります。2つの整数を使用すると、128までなどを処理できます。したがって、最大検出ステップは、必要な整数の数に隠れていますが小さい場合でも最適化できます。あなたはあなたの質問でが比較的小さいと言ったので、私はそれが数百万のオーダーではないと推測しました。ちなみに、long long intは、32ビットプロセッサ上でも、定義により常に少なくとも64ビットです。nO(m)mn
invalid_id 14

もちろん、C ++標準ではlong long int少なくとも64ビット整数型として定義されています。ただし、が巨大で、ワードサイズを(通常は)と表すと、それぞれに時間かかりますか?そして、合計ます。nww=64findO(nw)O(mnw)
mnbvmar 14

はい、残念ながら、値が大きい場合はそうです。だから今、私はあなたのケースでがどれくらい大きくなるか、それが境界付けられているかどうか疑問に思います。実際に数百万のオーダーである場合、このハックアラウンドはもう機能しませんが、値がの場合、値が小さい場合、高速で実質的にます。したがって、最適なアルゴリズムの選択は、通常どおり入力に依存します。たとえば、場合、と比較して実行時間であっても、通常、挿入ソートはマージソートよりも高速です。nncwncO(n+m)n100O(n2)O(nlogn)
invalid_id 14

3
書式設定の選択に困惑しています。ここでコードをタイプセットできることを知っていますか?
ラファエル

0

線形アルゴリズムはありませんが、これはO(m log m)のようです。

最初の座標と高さに基づいてセグメントを並べ替えます。これは、x1 <x2の場合は常に(x1、l1)が常に(x2、l2)の前に来ることを意味します。また、y1> y2の場合、高さy1の(x1、l1)は高さy2の(x1、l2)よりも前になります。

同じ最初の座標を持つサブセットごとに、次のことを行います。最初のセグメントを(x1、L)とします。サブセット内の他のすべてのセグメントの場合:セグメントが最初のセグメントよりも長い場合は、(x1、xt)から(L、xt)に変更し、適切な順序でLサブセットに追加します。それ以外の場合はドロップします。最後に、次のサブセットの最初の座標がLより小さい場合、(x1、L)を(x1、x2)と(x2、L)に分割します。(x2、L)を次のサブセットに正しい順序で追加します。サブセットの最初のセグメントがより高く、(x1、L)からの範囲をカバーするため、これを行うことができます。この新しいセグメントは(L、x2)をカバーするセグメントかもしれませんが、最初の座標Lを持つサブセットを見るまでそのことはわかりません。

すべてのサブセットを実行すると、重複しない一連のセグメントが作成されます。特定のXに対するY値が何であるかを判断するには、残りのセグメントを実行するだけです。

複雑さは次のとおりです。ソートはO(m log m)です。サブセットのループはO(m)です。ルックアップもO(m)です。

したがって、このアルゴリズムはnに依存しないようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.