3

KD ツリー (静的ケース) を構築しようとしています。ポイントは x 座標と y 座標の両方でソートされていると仮定します。

再帰の深さを均一にするために、セットは中央の x 座標を通る垂直線で 2 つのサブセットに分割されます。

再帰の深さが奇数の場合、セットは 2 つのサブセットに分割され、水平線は中央の y 座標を通過します。

中央値は、x / y 座標に従ってソートされたセットから決定できます。このステップは、セットを分割する前に行っています。そして、それがツリーの構築を遅らせる原因になっていると思います。

  1. コードをチェックして最適化するのを手伝ってくれませんか?
  2. k 番目の最近傍が見つかりません。誰かコードを手伝ってくれませんか?

あなたの助けと忍耐に感謝します...

サンプル コードを参照してください。

class KDNode
{
private:
Point2D *data;
KDNode *left;
KDNode *right;
    ....
};

void KDTree::createKDTree(Points2DList *pl)
{
//Create list
KDList kd_list;

//Create KD list (all input points)
for (unsigned int i = 0; i < pl->size(); i++)
{
kd_list.push_back((*pl)[i]);
}

//Sort points by x
std::sort(kd_list.begin(), kd_list.end(), sortPoints2DByY());

//Build KD Tree
root = buildKDTree(&kd_list, 1);
}


KDNode * KDTree::buildKDTree(KDList *kd_list, const unsigned int depth)
{
//Build KD tree
const unsigned int n = kd_list->size();

 //No leaf will be built
 if (n == 0)
 {
  return NULL;
 }

 //Only one point: create leaf of KD Tree
 else if (n == 1)
 {
  //Create one leaft
  return new KDNode(new Point2D ((*kd_list)[0]));
 }

 //At least 2 points: create one leaf, split tree into left and right subtree
 else
 {
  //New KD node
  KDNode *node = NULL;

  //Get median index
  const unsigned int median_index = n/2;

  //Create new KD Lists
  KDList kd_list1, kd_list2;

  //The depth is even, process by x coordinate
  if (depth%2 == 0)
  {
   //Create new median node
   node = new KDNode(new Point2D( (*kd_list)[median_index]));

   //Split list
   for (unsigned int i = 0; i < n; i++)
   {
    //Geta actual point
    Point2D *p = &(*kd_list)[i];

    //Add point to the first list: x < median.x
    if (p->getX() < (*kd_list)[median_index].getX())
    {
     kd_list1.push_back(*p);
    }

    //Add point to the second list: x > median.x
    else if (p->getX() > (*kd_list)[median_index].getX())
    {
     kd_list2.push_back(*p);
    }
   }

   //Sort points by y for the next recursion step: slow construction of the tree???
   std::sort(kd_list1.begin(), kd_list1.end(), sortPoints2DByY());
   std::sort(kd_list2.begin(), kd_list2.end(), sortPoints2DByY());

  }

  //The depth is odd, process by y coordinates
  else
  {

   //Create new median node
   node = new KDNode(new Point2D((*kd_list)[median_index]));

   //Split list
   for (unsigned int i = 0; i < n; i++)
   {
    //Geta actual point
    Point2D *p = &(*kd_list)[i];

    //Add point to the first list: y < median.y
    if (p->getY() < (*kd_list)[median_index].getY())
    {
     kd_list1.push_back(*p);
    }

    //Add point to the second list: y < median.y
    else if (p->getY() >(*kd_list)[median_index].getY())
    {
     kd_list2.push_back(*p);
    }
   }

   //Sort points by x for the next recursion step: slow construction of the tree???
   std::sort(kd_list1.begin(), kd_list1.end(), sortPoints2DByX());
   std::sort(kd_list2.begin(), kd_list2.end(), sortPoints2DByX());

  }

  //Build left subtree
  node->setLeft( buildKDTree(&kd_list1, depth +1 ) );

  //Build right subtree
  node->setRight( buildKDTree(&kd_list2, depth + 1 ) );

  //Return new node 
  return node; 
 }
}
4

4 に答える 4

5

中央値を見つけるための並べ替えは、おそらくここで最悪の原因です。これは、問題が O(n) 時間で解決できるのに対し、それは O(nlogn) であるためです。代わりに nth_element を使用する必要があります: http://www.cplusplus.com/reference/algorithm/nth_element/。これにより、平均して線形時間の中央値が見つかり、その後、線形時間でベクトルを分割できます。

ベクトルのメモリ管理も、ベクトルのサイズが 2 倍になるたびにすべての要素を移動する必要があるため、特に大きなベクトルの場合、多くの時間がかかる可能性があります。vector の reserve メソッドを使用して、新しく作成されたノード内の vector に正確に十分なスペースを予約できるため、push_back で新しいものが追加されても動的に増やす必要はありません。

また、絶対に最高のパフォーマンスが必要な場合は、低レベルのコードを使用して、ベクトルを廃止し、代わりに単純な配列を予約する必要があります。N番目の要素または「選択」アルゴリズムはすぐに利用でき、自分で書くのはそれほど難しくありません: http://en.wikipedia.org/wiki/Selection_algorithm

于 2011-02-15T12:16:36.217 に答える
3

kdツリーを最適化するためのヒント:

  • QuickSelectなどの線形時間中央値検出アルゴリズムを使用します。
  • 実際に「ノード」オブジェクトを使用することは避けてください。ポイントのみを使用してツリー全体を保存でき、追加情報はゼロです。基本的には、オブジェクトの配列を並べ替えるだけです。ルートノードは中央になります。ルートを最初に配置し、次にヒープレイアウトを使用する再配置は、クエリ時にCPUメモリキャッシュに適している可能性がありますが、構築するのはより困難です。
于 2013-01-01T16:22:43.187 に答える
3

あなたの質問に対する答えではありませんが、http://ompf.org/forum/のフォーラムを強くお勧めします。 彼らは、さまざまなコンテキストでの高速な kd-tree 構築について素晴らしい議論を行っています。ひょっとしたら、そこからインスピレーションを得られるかもしれません。

編集:
OMPF フォーラムは廃止されましたが、直接の代替は現在http://ompf2.com/で利用できます。

于 2010-11-17T10:43:10.787 に答える