1

よりバランスの取れたツリーを構築するための「リストの中央値」アルゴリズムを使用して、JavaでKDツリーをコーディングしました。ウィキによって提供されたデータを使用する場合は正常に機能するようです。ウィキペディアの例ではX、Y値のみを使用するため、Z深度は評価されないことに注意してください。

ウィキペディアから:

point_list = [(2,3), (5,4), (9,6), (4,7), (8,1), (7,2)]

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

私のJavaプログラムから:

depth=0 id=(7.0, 2.0, 0.0)
├── [left] depth=1 id=(5.0, 4.0, 0.0)
│   ├── [left] depth=2 id=(2.0, 3.0, 0.0)
│   └── [right] depth=2 id=(4.0, 7.0, 0.0)
└── [right] depth=1 id=(9.0, 6.0, 0.0)
    └── [left] depth=2 id=(8.0, 1.0, 0.0)

しかし、このデータに「リストの中央値」アプローチを使用すると、正しく機能していないようです。

point list = [(1,0,-1), (1,0,-2), (1,0,1), (1,0,2)]

私はこのような木を手に入れます:

depth=0 id=(1.0, 0.0, 1.0)
├── [left] depth=1 id=(1.0, 0.0, -2.0)
│   └── [left] depth=2 id=(1.0, 0.0, -1.0)
└── [right] depth=1 id=(1.0, 0.0, 2.0)

(1.0、0.0、2.0)は(1.0、0.0、1.0)の右側にあるため、これは正しくないように見えますが、Y値が等しいため、本質的に等しくなります。また、(1.0、0.0、-1.0)は(1.0、0.0、-2.0)の左側にあり、Z値が大きいため、右側にある必要があります。

問題は、X値とY値が等しく、Z値が可変であることに起因すると思います。そのため、リストの中央値は、リストを正確に分割しません。

...ウィキのPythonコードに続く元のコード..。

private static KdNode createNode(List<XYZPoint> list, int k, int depth) {
    if (list == null || list.size() == 0) return null;

    int axis = depth % k;
    if (axis == X_AXIS) Collections.sort(list, X_COMPARATOR);
    else if (axis == Y_AXIS) Collections.sort(list, Y_COMPARATOR);
    else Collections.sort(list, Z_COMPARATOR);

    KdNode node = null;
    if (list.size() > 0) {
        int mediaIndex = list.size() / 2;
        node = new KdNode(k, depth, list.get(mediaIndex));
        if ((mediaIndex - 1) >= 0) {
            List<XYZPoint> less = list.subList(0, mediaIndex);
            if (less.size() > 0) {
                node.lesser = createNode(less, k, depth + 1);
                node.lesser.parent = node;
            }
        }
        if ((mediaIndex + 1) <= (list.size() - 1)) {
            List<XYZPoint> more = list.subList(mediaIndex + 1, list.size());
            if (more.size() > 0) {
                node.greater = createNode(more, k, depth + 1);
                node.greater.parent = node;
            }
        }
    }

    return node;
}

...私のコメントに基づく新しいコード..。

private static KdNode createNode(List<XYZPoint> list, int k, int depth) {
    if (list == null || list.size() == 0) return null;

    int axis = depth % k;
    if (axis == X_AXIS) Collections.sort(list, X_COMPARATOR);
    else if (axis == Y_AXIS) Collections.sort(list, Y_COMPARATOR);
    else Collections.sort(list, Z_COMPARATOR);

    KdNode node = null;
    if (list.size() > 0) {
        int medianIndex = list.size() / 2;
        node = new KdNode(k, depth, list.get(medianIndex));
        List<XYZPoint> less = new ArrayList<XYZPoint>(list.size()-1);
        List<XYZPoint> more = new ArrayList<XYZPoint>(list.size()-1);
        //Process list to see where each non-median point lies
        for (int i=0; i<list.size(); i++) {
            if (i==medianIndex) continue;
            XYZPoint p = list.get(i);
            if (KdNode.compareTo(depth, k, p, node.id)<=0) {
                less.add(p);
            } else {
                more.add(p);
            }
        }
        if (less.size() > 0) {
            node.lesser = createNode(less, k, depth + 1);
            node.lesser.parent = node;
        }
        if (more.size() > 0) {
            node.greater = createNode(more, k, depth + 1);
            node.greater.parent = node;
        }
    }
4

2 に答える 2

2

問題は確かに等しい座標に関係しており、ノードをパーツに分割する方法から発生しlessますmore。中央値のインデックスがあるので、座標をチェックする代わりに分割にインデックスを使用してみませんか? createNode116行目の条件を次のように変更するだけです

if (KdNode.compareTo(depth, k, p, node.id)<=0) {

if (i<medianIndex) {

ところで、ソートよりもリストを下位、中央値、上位に分割するためのより効率的なアルゴリズムがあります。(下部と上部はソートする必要はありません! std::nth_elementC++ stdlib の実装などを参照してください。申し訳ありませんが、私は Java プログラミングに夢中です)

于 2012-11-22T17:59:11.243 に答える
0

この時点での重要な質問は、KD ツリーで正確に何をしたいのかということだと思います。

  • X距離とY距離のみを使用して最も近い点を見つけたいだけの場合、アルゴリズムは完全に問題ありません.例から同じXY距離にある4つの点のうち少なくとも1つを見つけることができます.
  • XY 距離で最も近い点をすべて見つけたい場合は、KD ツリー構築関数を同じに保ちますが、ルックアップ関数のすべての「<」演算子を「<=」に変更するだけです。クエリ ポイントとまったく同じ KD ツリー ポイントが見つかった場合でも、リーフが見つかるまで、そのツリーの任意の子を下っていく必要があります。次に、KD ツリーで通常どおりツリーを上っていき、兄弟ツリーがこれまでに見つけた最短距離と一致する可能性がある場合は常に兄弟ツリーを下ります。
  • X、Y、および Z 座標を含む距離を使用する場合は、ツリーを 3 次元の KD ツリーにする必要があります。次に分割するディメンションを選択するためのスキーム)。
于 2012-11-22T20:36:07.003 に答える