重複した四分木


10

四分木を実装しています。このデータ構造を知らない人のために、次の小さな説明を含めます。

クワッドツリーはデータ構造であり、3次元空間でのオクトリーと同じようにユークリッド平面にあります。クワッドツリーの一般的な用途は、空間インデックスです。

それらがどのように機能するかを要約すると、クワッドツリーは、最大容量と初期バウンディングボックスを持つコレクションです(ここでは長方形としましょう)。最大容量に達したクワッドツリーに要素を挿入しようとすると、クワッドツリーは4つのクワッドツリーに分割されます(その幾何学的表現は、挿入前のツリーの4分の1の面積になります)。各要素は、その位置に応じてサブツリーに再配布されます。長方形を操作するときの左上の境界。

したがって、クワッドツリーはリーフであり、その容量よりも要素が少ないか、4つのクワッドツリーを子として持つツリー(通常、北西、北東、南西、南東)です。

私の懸念は、重複を追加しようとした場合、同じ要素が数回または同じ位置にあるいくつかの異なる要素である場合、四分木はエッジの処理に根本的な問題があることです。

たとえば、容量が1の四分木と、境界ボックスとして単位長方形を使用する場合:

[(0,0),(0,1),(1,1),(1,0)]

そして、左上の境界が原点である長方形を2回挿入しようとします(または、N> 1の容量を持つ四分木にN + 1回挿入しようとした場合も同様です)。

quadtree->insert(0.0, 0.0, 0.1, 0.1)
quadtree->insert(0.0, 0.0, 0.1, 0.1)

最初の挿入は問題になりません: 最初の挿入

ただし、最初の挿入でサブディビジョンがトリガーされます(容量が1であるため)。 2番目の挿入、最初のサブディビジョン

したがって、両方の長方形は同じサブツリーに配置されます。

次に、2つの要素が同じ四分木に到着し、サブディビジョンをトリガーします… 2番目の挿入、2番目の分割

以下同様に、サブディビジョンメソッドは無期限に実行されます。なぜなら、(0、0)は、作成された4つのうち常に同じサブツリーにあるため、無限再帰問題が発生するためです。

重複した四分木を持つことは可能ですか?(そうでない場合、それをとして実装できますSet

四分木のアーキテクチャを完全に壊すことなく、この問題をどのように解決できますか?


どのように動作させますか?あなたはそれを実装しているので、どの動作があなたにとって正しいかを決定しなければなりません。おそらく、それぞれの一意の座標は、その座標の要素のリストである可能性があります。多分あなたのポイントはユニークであることに制限されています。あなたはあなたが何を必要としているかを知っています、そして私達は知りません。
2014年

@役に立たないそれは本当です。しかし、このトピックについてはかなり多くの研究が行われているはずであり、私も実際にハンドルを再発明したくありません。TBH、私はまだ...この質問は、より多くのSO上、programmers.SE上、gamedev.SE上あるいはmath.SEに属しているかどうかわからない
ピエールArlaud

また、meta.programmers.stackexchange.com / questions / 6709 / …を参照してください。…
Pierre Arlaud 14年

回答:


3

データ構造を実装しているので、実装を決定する必要があります。

四分木に一意性について具体的なことを言わない限り(そして、私がそれを知っているわけではない場合)、これは実装の決定です。これは四分木の定義と直交しており、好きなように処理することを選択できます。クワッドツリーは、キーを挿入および更新する方法を示しますが、キーが一意である必要があるかどうか、または各ノードに何をアタッチできるかは示しません。

実装の決定を行うことは、車輪を再発明することではありません。少なくとも、最初に独自の実装を作成する以上のことはありません。

比較のために、C ++標準ライブラリは、一意のセット、一意でないマルチセット、一意のマップ(基本的に、キーによってのみ順序付けおよび比較される一連のキーと値のペア)、および一意でないマルチマップを提供します。これらはすべて同じ赤黒ツリーを使用して通常実装され、アーキテクチャ壊すものはありません。これは、赤黒ツリーの定義が、キーの一意性やリーフノードに格納されているタイプについて何も言えないためです。

最後に、これに関する研究があると思われる場合は、それを見つけてから、それについて話し合います。多分私が見落とした四分木不変式、またはより良いパフォーマンスを可能にする追加の制約があります。


私の問題は、一意性が要件であると述べているドキュメントが見つからないことです。それでも、私の例を見たなら、同じ要素を何回か含めた場合、それが実際の問題であることがわかります。
Pierre Arlaud、2014年

ツリータイプの構造の場合、値を持つノードには、重複の増分と減分だけを行う「カウント」フィールドも時々与えられませんか?
Jトラナ2014年

2

ここで誤解があると思います。

私が理解しているように、すべての四分木ノードには、ポイントによってインデックスが付けられた値が含まれています。つまり、トリプル(x、y、value)が含まれています。

また、nullの場合がある子ノードへの4つのポインタも含まれます。キーと子リンクの間にはアルゴリズムの関係があります。

インサートは次のようになります。

quadtree->insert(0.0, 0.0, value1)
quadtree->insert(0.0, 0.0, value2)

最初の挿入は(親)ノードを作成し、そこに値を挿入します。

2番目の挿入では、子ノードを作成してリンクし、値を挿入します(最初の値と同じになる場合があります)。

どの子ノードがインスタンス化されるかは、アルゴリズムによって異なります。アルゴリズムが[x)の形式で、座標空間が[0,1)の範囲内にある場合、各子は[0,0.5)の範囲に広がり、ポイントはNW子に配置されます。

無限の再帰は見られません。


それで、細分するときに子クワッドツリーにノード再配布する私の方法は、私の実装の何が問題なのですか?
Pierre Arlaud、2014年

おそらく問題は、値を(親の)ある場所から(子の)より良い場所に移動しようとしていることです。これは実際に行われる方法ではありません。値はどこにでも結構です。ただし、2つの同一のポイントを異なるノードに配置できるという興味深い結果がもたらされます(ただし、常に関連する親と子)。
david.pfx 2014年

2

私が遭遇した一般的な解決策(ゲームではなく視覚化の問題)は、常に置き換えられるか、決して置き換えられないかのいずれかのポイントを捨てることです。

やりやすいのがポイントのほうがいいと思います。


2

ほぼ同じサイズの要素のインデックスを作成していると仮定します。そうしないと、人生が複雑になるか、遅くなるか、またはその両方になります……

Quadtreeノードは、固定容量を持つ必要ありません。容量は、

  • メモリまたはディスク上で各ツリーノードを固定サイズにできるようにします。ツリーノードに可変サイズの要素のセットが含まれ、対応するスペース割り当てシステムを使用している場合は不要です。(例えば、メモリ内のjava / c#オブジェクト。)
  • ノードを分割するタイミングを決定します。
    • ルールを再定義するだけで、ノードが「n」個を超える地区要素を含む場合に分割され、地区は要素の場所に従って定義されます。
    • または、「複合要素」を使用して、同じ場所に乗算要素がある場合は、これらの乗算要素のリストを含む新しい要素を導入します。

2

空間インデックスの問題を扱っているときは、実際には空間ハッシュから始めるか、私の個人的なお気に入りであるプレーングリッドから始めることをお勧めします。

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

...そして、スパース表現を可能にするツリー構造に移行する前に、まずその弱点を理解します。

明らかな弱点の1つは、多くの空のセルでメモリを浪費する可能性があることです(適切に実装されたグリッドでは、実際に挿入するノードが数十億個もない限り、セルあたり32ビットを超える必要はありません)。もう1つは、セルのサイズより大きく、たとえば数十のセルにまたがる中程度のサイズの要素がある場合、それらの中型の要素を理想よりもはるかに多くのセルに挿入すると、大量のメモリを浪費する可能性があることです。同様に、空間クエリを実行する場合、理想よりも多くのセルをチェックする必要がある場合があります。

しかし、特定の入力に対して可能な限り最適化するためにグリッドを調整する唯一のものはcell sizeであり、それはあなたが考えたりいじったりするためにあまり多くを残すことはありません、そしてそれが私の頼りになるデータ構造です使用しない理由が見つかるまで、空間インデックスの問題。実装は簡単で、実行時の入力を1つ以上操作する必要はありません。

あなたは普通の古いグリッドから多くを得ることができます、そして私は実際にそれらを普通の古いグリッドで置き換えることによって商用ソフトウェアで使用されている多くのクアッドツリーとkdツリーの実装を打ち負かしました(それらは必ずしも最良に実装されたものではありませんでしたが) 、しかし著者たちはグリッドを作成するのに費やした20分よりもずっと多くの時間を費やした)。衝突検出用のグリッドを使用して他の場所で質問に回答するためにホイップした簡単なことを次に示します(実際には最適化されていなくても、数時間の作業で済みます)。質問に答えるためにパスファインディングがどのように機能するかを学ぶために、ほとんどの時間を費やす必要がありました。また、この種の衝突検出を実装したのは、これが初めてでした)。

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

グリッドのもう1つの弱点(ただし、多くの空間インデックス構造の一般的な弱点です)は、同じ位置にある多くのポイントのように、多数の一致する要素または重複する要素を挿入すると、まったく同じセルに挿入されます)、そのセルを通過するときにパフォーマンスを低下させます。同様に、セルサイズよりもはるかに大きい大量の要素を挿入する場合、それらは大量のセルに挿入され、大量のメモリを使用し、全体的な空間クエリに必要な時間を短縮する必要があります。 。

ただし、偶然の大規模な要素に関する上記の2つの直接的な問題は、実際にはすべての空間インデックス構造で問題になります。プレーンな古いグリッドは実際にはこれらの病理学的なケースを他の多くのケースよりも少しだけうまく処理します。少なくともセルを繰り返し再分割することを望まないからです。

グリッドから始めて、四分木やKDツリーのようなものに取り掛かるとき、解決したい主な問題は、要素が多すぎるセルに挿入されている、多すぎるセルを持っている、という問題です。 /またはこのタイプの密な表現であまりにも多くのセルをチェックする必要があります。

しかし、四分木をグリッドの最適化と考えると特定のユースケースでは、 "最小セルサイズ"の考え方を考慮して、四分木ノードの再帰的なサブディビジョンの深さを制限することが役立ちます。これを行うと、四分木の最悪のシナリオでも葉の密度の高いグリッドに分解されますが、ルートからグリッドセルに移動するのに対数時間を必要としないため、グリッドよりも効率が悪いだけです。一定時間。それでも、その最小セルサイズを考えることで、無限ループ/再帰シナリオを回避できます。大規模な要素の場合、必ずしも均等に分割されず、子ノードのAABBが重複する可能性があるルーズクワッドツリーなどの代替バリアントもあります。BVHは、ノードを均等に細分割しない空間インデックス構造としても興味深いものです。ツリー構造に対する一致要素の場合、主なことは、サブディビジョンに制限を課すことです(または他の人が示唆したように、それらを拒否するか、またはいつ葉を特定するときに葉の要素の一意の数に寄与していないかのようにそれらを扱う方法を見つけることです)細分化する必要があります)。ノードが中央分離する必要があるかどうかを判断するときに1つの次元のみを考慮する必要があるため、Kdツリーは、多くの一致する要素を持つ入力を予測する場合にも役立ちます。


quadtreesの更新として、衝突検出で効率的にする方法について誰かが質問(やっぱり好きです)に質問し、結局、実装方法について根性を漏らしてしまいました。また、あなたの質問にも答えるはずです:stackoverflow.com/questions/41946007/…–
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.