簡體   English   中英

O(kn log n)的算法平衡KD樹

[英]Algorithm balanced K-D tree with O(kn log n)

我試圖用O(kn log n)實現一個平衡KD樹,我使用了預排序的K數組(每個索引的排序數組)來獲取O(kn log n),並使用中值來獲取平衡樹。

我面臨的問題是,大多數中間值在某個級別(例如x軸的中間值)可能會在另一個后續級別(例如y軸)上再次選擇。

我試圖通過使用選定的x值作為樞軸將y排序的數組划分為兩個數組來解決此問題,但是這種方式不會產生平衡的樹。

任何想法如何獲得O(kn log n)的KD平衡樹?

編輯

引自Wiki https://en.wikipedia.org/wiki/K-d_tree

用於構建平衡kd樹的替代算法會在構建樹之前對數據進行預排序。 然后,他們在樹的構建過程中保持預排序的順序,因此省去了在每個細分級別查找中位數的昂貴步驟。 兩種這樣的算法構建了一個平衡的kd樹來對三角形進行排序,以改善三維計算機圖形的光線跟蹤的執行時間。 這些算法在構建kd樹之前對n個三角形進行預排序,然后在最佳情況下以O(n log n)時間構建樹。[5] [6] 建立平衡的kd樹以對點進行排序的算法的最壞情況下的復雜度為O(kn log n)。[7] 該算法在構建樹之前使用O(n log n)排序(例如Heapsort或Mergesort)對k個維度中的n個點進行預排序。 然后,它在樹構建過程中保持這k個預排序的順序,從而避免在每個細分級別找到中位數。

任何人都可以提供上面提供的這種算法嗎?

編輯

提出了一種方法,但是如果中位數的特定軸存在任何重復值,則該方法將不起作用。

例如

x1 = [(0,7),(1,3),(3,0),(3,1),(6,2)] y1 = [(3,0),(3,1),(6 ,2),(1,3),(0,7)]

x軸的中位數為3。因此,當我們要分割數組y11和y12時,我們必須使用>和<來將y數組左右分配,並將樞軸作為定界符。

如果重復特定軸上的中位數a,則不能保證其中之一是正確的

考慮x軸上的分區,在完成第一步分區的上述示例后,x1陣列上沒有問題:

median=(3,0)
The pivot = 3 // is it's the median of x axis
y11[],y12[] 
for(i = 0 ; i < x1.size;i++)
  if(y1[i].getX()<pivot)
    y11.add(y1[i])
  else 
    if(y1[i].getX()>pivot)
     y12.add(y1[i])

這將導致y11 = [(2,1),(1,3),(0,7)] y12 = [(6,2)]

任何想法如何處理這種情況? 還是有其他預排序kd-tree預排序算法O(kn log n)?

拆分數據時,您需要保留排序順序。

例如,使用我們構建的數據(x,y)

x1 = [ (0, 7), (1, 3), (3, 0), (4, 2), (6, 1) ]
y1 = [ (3, 0), (6, 1), (3, 2), (1, 3), (0, 7) ]

如果我們現在在x處拆分,則需要通過x=3,y=0處的記錄來過濾這兩個集合。

即拆分兩個列表,刪除(3,0) ,所有x<3都進入第一個列表,所有x>3去第二個(順序不變):

x1 -> filter to  x11 = [ (0, 7), (1, 3) ]  x12 = [ (4, 2), (6, 1) ]
y1 -> filter to  y11 = [ (1, 3), (0, 7) ]  y12 = [ (6, 1), (4, 2) ]

重點是通過x值過濾每個排序的列表,同時保持排序順序(因此,在每個O(log n)級別中都在O(n * k)中)。 如果僅使用x1,並從x1重建y11和y12,則需要再次排序。 根據需要,這與按x一次,按y一次排序相同。 除此之外,我們沒有再排序,只能選擇。

我認為這在實踐中不會更好。 排序比額外的內存便宜。

要詳細說明我的評論(可能還有Anony-Mousse的回答 ):

在構造KD樹時進行預排序的關鍵思想是在拆分過程中保持順序。 開銷看起來很高,按順序進行重新排序(和k-select )的比較基准。
一些原理證明的Java源代碼:

package net.*.coder.greybeard.sandbox;

import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;

/** finger exercise pre-sorting & split for KD-tree construction
 *  (re. https://stackoverflow.com/q/35225509/3789665) */
public class KDPreSort {
 /** K-dimensional key, dimensions fixed
  *   by number of coordinates in construction */
    static class KKey {
        public static KKey[] NONE = {};
        final Comparable[]coordinates;
        public KKey(Comparable ...coordinates) {
            this.coordinates = coordinates;
        }
    /** @return {@code Comparator<KKey>} for coordinate {@code n}*/
        static Comparator<KKey> comparator(int n) { // could be cached
            return new Comparator<KDPreSort.KKey>() { @Override
                    public int compare(KKey l, KKey r) {
                        return l.coordinates[n]
                            .compareTo(r.coordinates[n]);
                    }
                };
        }
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(
                Arrays.deepToString(coordinates));
            sb.setCharAt(0, '(');
            sb.setCharAt(sb.length()-1, ')');
            return sb.toString();
        }
    }

 // static boolean trimLists = true; // introduced when ArrayList was used in interface

/** @return two arrays of {@code KKey}s: comparing smaller than
 *    or equal to {@code pivot} (according to {@code comp)},
 *    and greater than pivot -
 *    in the same order as in {@code keys}. */
    static KKey[][] split(KKey[] keys, KKey pivot, Comparator<KKey> comp) {
        int length = keys.length;
        ArrayList<KKey>
            se = new ArrayList<>(length),
            g = new ArrayList<>(length);
        for (KKey k: keys) {
        // pick List to add to
            List<KKey>d = comp.compare(k, pivot) <= 0 ? se : g;
            d.add(k);
        }
//      if (trimLists) { se.trimToSize(); g.trimToSize(); }
        return new KKey[][] { se.toArray(KKey.NONE), g.toArray(KKey.NONE) };
    }
 /** @return two arrays of <em>k</em> arrays of {@code KKey}s:
  *  comparing smaller than or equal to {@code pivot}
  *   (according to {@code comp)}, and greater than pivot,
  *  in the same order as in {@code keysByCoordinate}. */
    static KKey[][][]
        splits(KKey[][] keysByCoordinate, KKey pivot, Comparator<KKey> comp) {
        final int length = keysByCoordinate.length;
        KKey[][]
            se = new KKey[length][],
            g = new KKey[length][],
            splits;
        for (int i = 0 ; i < length ; i++) {
            splits = split(keysByCoordinate[i], pivot, comp);
            se[i] = splits[0];
            g[i] = splits[1];
        }
        return new KKey[][][] { se, g };
    }
 // demo
    public static void main(String[] args) {
    // from https://stackoverflow.com/q/17021379/3789665
        Integer [][]coPairs = {// {0, 7}, {1, 3}, {3, 0}, {3, 1}, {6, 2},
                {12, 21}, {13, 27}, {19, 5}, {39, 5}, {49, 63}, {43, 45}, {41, 22}, {27, 7}, {20, 12}, {32, 11}, {24, 56},
            };
        KKey[] someKeys = new KKey[coPairs.length];
        for (int i = 0; i < coPairs.length; i++) {
            someKeys[i] = new KKey(coPairs[i]);
        }
    //presort
        Arrays.sort(someKeys, KKey.comparator(0));
        List<KKey> x = new ArrayList<>(Arrays.asList(someKeys));
        System.out.println("by x: " + x);
        KKey pivot = someKeys[someKeys.length/2];
        Arrays.sort(someKeys, KKey.comparator(1));
        System.out.println("by y: " + Arrays.deepToString(someKeys));
    // split by x
        KKey[][] allOrdered = new KKey[][] { x.toArray(KKey.NONE), someKeys },
            xSplits[] = splits(allOrdered, pivot, KKey.comparator(0));
        for (KKey[][] c: xSplits)
            System.out.println("split by x of " + pivot + ": "
                + Arrays.deepToString(c));
    // split "higher x" by y
        pivot = xSplits[1][1][xSplits[1][1].length/2];
        KKey[][] ySplits[] = splits(xSplits[1], pivot, KKey.comparator(1));
        for (KKey[][] c: ySplits)
            System.out.println("split by y of " + pivot + ": "
                + Arrays.deepToString(c));
    }
}

(在SE上沒有找到合適的答案/實現方式,而沒有投入太多精力。輸出結果與您的示例不符,對於較長的示例,我不得不重新格式化以使其相信。
該代碼看起來很丑陋,因為它可能 :如果很想重新閱讀SE上發布的代碼許可證 ,請訪問Code Review 。)(考慮到有投票,接受和授予賞金,然后重新訪問Anony) -慕斯的答案。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM