web-dev-qa-db-ja.com

2Dグリッド上で最も近いオブジェクトを見つけるためのアルゴリズム

グリッド上の各スポットにx個のオブジェクト(x> = 0)がある2Dグリッドがあるとします。私は考えるのに苦労しています 掃除 ユーザーが座標を指定すると、アルゴリズムがオブジェクトに最も近い座標(指定された座標を含む)を見つけるようにするアルゴリズム。

簡単にするために、2つの座標が同じ距離にある場合、最初の座標が返されると仮定します(または、アルゴリズムがこのように機能しない場合は、最後の座標は関係ありません)。

編集:1離れている座標は、上、下、左、または右のいずれかである必要があります。対角線上にある座標は2です。

ちなみに、アルゴリズムの優れた無料のオンラインリファレンスは何ですか?

23
random

更新

新しい情報:

対角線上の座標が2離れていると仮定します

このアルゴリズムは機能します。アルゴリズムは、内側から開始された各「リング」の各ポイントをテストするスパイラルのような方法で外側を検索します。

範囲外の状況は処理しないことに注意してください。したがって、ニーズに合わせてこれを変更する必要があります。

int xs, ys; // Start coordinates

// Check point (xs, ys)

for (int d = 1; d<maxDistance; d++)
{
    for (int i = 0; i < d + 1; i++)
    {
        int x1 = xs - d + i;
        int y1 = ys - i;

        // Check point (x1, y1)

        int x2 = xs + d - i;
        int y2 = ys + i;

        // Check point (x2, y2)
    }


    for (int i = 1; i < d; i++)
    {
        int x1 = xs - i;
        int y1 = ys + d - i;

        // Check point (x1, y1)

        int x2 = xs + i;
        int y2 = ys - d + i;

        // Check point (x2, y2)
    }
}

旧バージョン

2Dグリッドで、(0、0)と(1、0)の間の距離が(0、0)と(1、1)と同じであると仮定します。このアルゴリズムは機能します。アルゴリズムは、内側から開始された各「リング」の各ポイントをテストするスパイラルのような方法で外側を検索します。

範囲外の状況は処理しないことに注意してください。したがって、ニーズに合わせてこれを変更する必要があります。

int xs, ys; // Start coordinates

if (CheckPoint(xs, ys) == true)
{
    return (xs, ys);
}

for (int d = 0; d<maxDistance; d++)
{
    for (int x = xs-d; x < xs+d+1; x++)
    {
        // Point to check: (x, ys - d) and (x, ys + d) 
        if (CheckPoint(x, ys - d) == true)
        {
            return (x, ys - d);
        }

        if (CheckPoint(x, ys + d) == true)
        {
            return (x, ys - d);
        }
    }

    for (int y = ys-d+1; y < ys+d; y++)
    {
        // Point to check = (xs - d, y) and (xs + d, y) 
        if (CheckPoint(x, ys - d) == true)
        {
            return (xs - d, y);
        }

        if (CheckPoint(x, ys + d) == true)
        {
            return (xs - d, y);
        }
    }
}

オブジェクトのリストがある場合

リスト内のすべてのオブジェクトのすべての位置がある場合、すべての空の正方形を検索する必要がなく、 2D距離計算 を実行して最も近い正方形を決定できるため、これははるかに簡単です。あなたへ。オブジェクトのリストをループして、次のように距離を計算します。

Define your two points. Point 1 at (x1, y1) and Point 2 at (x2, y2).

    xd = x2-x1
    yd = y2-y1
    Distance = SquareRoot(xd*xd + yd*yd)

次に、距離が最も短いものを選択するだけです。

2D配列しかない場合

ただし、説明した問題が、最初にすべてのオブジェクトを検索しないとオブジェクトの場所を一覧表示できない2D配列を想定している場合は、スパイラルループを実行する必要があります。

' スパイラル検索方法 'を検索すると、いくつかの興味深いリンクが表示されます。 ここにスパイラルループを実行するコードがあります 配列の周りですが、これは任意のポイントからは機能せず、外側にスパイラルしますが、目的を達成する方法についていくつかの良いアイデアが得られるはずです。

これは 同様の質問 2D配列にスパイラル順序で値を入力することについてです。

とにかく、これが私が問題に取り組む方法です:

ポイントPを指定して、Pの周囲の領域を指定するベクトルペアを作成します。

したがって、P = 4,4その場合あなたのベクトルペアは3,3|5,5

それらの境界で各値をループします。

for x = 3 to 5
    for y = 3 to 5
        check(x,y)
    next
next

値が見つかった場合は、終了します。そうでない場合は、境界をもう一度1つ増やします。したがって、この場合、2,2 | 6,6に移動します。

ループして値を確認するときは、負のインデックスが作成されていないこと、および配列のサイズを超えていないことを確認してください。

また、境界をn回拡張する場合は、外側の境界値をループするだけでよく、内側の値を再チェックする必要はありません。

どちらの方法が速いですか?

それはすべて依存します:

  • アレイの密度
  • 流通技術
  • オブジェクトの数

配列の密度

2つのオブジェクトを含む500x500配列がある場合、リストのループは常にスパイラルの実行よりもパフォーマンスが高くなります

配布テクニック

分布のパターンがある場合(つまり、オブジェクトが互いにクラスター化する傾向がある場合)、スパイラルのパフォーマンスが向上する可能性があります。

オブジェクトの数

リスト手法ではすべての距離をチェックして計算する必要があるため、オブジェクトが100万個ある場合、スパイラルのパフォーマンスはおそらく速くなります。

リストソリューションは毎回すべてのオブジェクトをチェックする必要があるという事実と比較して、スペースが埋められる確率を計算することにより、最速のソリューションを計算できるはずです。

ただし、リスト手法を使用すると、パフォーマンスを向上させるためにスマートな並べ替えを実行できる場合があります。おそらく調べる価値があります。

13
Tom Gullen

オブジェクトが密集している場合は、近くのポイントを検索するだけで、中心かららせん状に広がる最も近いオブジェクトを見つけることができます。オブジェクトがまばらな場合は、 quadtree または関連するデータ構造(Rツリーなど)の方がおそらく優れています。これが writeup の例です。

良いオンラインアルゴリズムのリファレンスはわかりませんが、たまに1行以上のコードを書く場合は、ペニーを節約して購入することができます [〜#〜] clrs [〜#〜] お金の価値があります。この本に基づいて、 Peteris Krumins によって入念に注釈が付けられた講義がオンラインにありますが、それらは本の一部しかカバーしていません。これはあなたが所有する必要がある数少ない本の1つです。

5
deinst

次の簡単な解決策は、グリッドセルごとに追加情報を格納する余裕があり、グリッドに新しいオブジェクトを追加するための時間コストが比較的高くなることを前提としています。

アイデアは、すべてのセルが最も近い占有セルへの参照を保持するため、O(1)クエリ時間を許可します。オブジェクトが位置(i、j)に追加されるたびに、次のスキャンを実行します。周囲のセル、サイズが大きくなるリングをカバーします。スキャンされる各セルについて、現在最も近い占有セル参照を評価し、必要に応じて交換します。スキャンされる最後のリングがまったく変更されない場合、プロセスは終了します。最悪の場合プロセスはすべてのグリッドセルをスキャンしますが、グリッドが十分に密になると、最終的にはより良くなります。

このソリューションは実装が簡単で、(グリッドがメモリ内でどのように編成されているかによって)かなりのスペースオーバーヘッドが発生する可能性がありますが、最適なクエリ時間を提供します。

3
Eyal Schneider