web-dev-qa-db-ja.com

重複した四分木

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

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

それらがどのように機能するかを要約すると、クワッドツリーは、最大容量と初期バウンディングボックスを備えたコレクションです(ここでは長方形としましょう)。最大容量に達したクワッドツリーに要素を挿入しようとすると、クワッドツリーは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)

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

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

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

次に、2つの要素が同じ四分木に到着し、サブディビジョンをトリガーします… Second insert, second subdivison

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

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

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

10
Pierre Arlaud

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

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

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

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

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

3
Useless

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

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

2
Douglas Bagnall

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

私が理解しているように、すべての四分木ノードには、ポイントによってインデックスが付けられた値が含まれています。つまり、トリプル(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の子に配置されます。

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

2
david.pfx

空間インデックスの問題を処理する場合、実際には空間ハッシュまたは個人的なお気に入り、つまり古いプレーングリッドから始めることをお勧めします。

enter image description here

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

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

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

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

enter image description here

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

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

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

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

2
user204677

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

クワッドツリーノードは、固定容量を持つ必要はありません。容量は、

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