Hilbert R-Tree (wikipedia) (citeseer)が適切なデータ構造であると思われるアプリケーションがあります。具体的には、多くの更新が発生するデータ セットに対してかなり高速な空間クエリが必要です。
ただし、私が見る限り、このデータ構造のアルゴリズムの説明には、必要なヒルベルト値を実際に計算する方法についても言及されていません。これは、ヒルベルト曲線に沿った点までの距離です。
では、これを計算する方法について何か提案はありますか?
Hilbert R-Tree (wikipedia) (citeseer)が適切なデータ構造であると思われるアプリケーションがあります。具体的には、多くの更新が発生するデータ セットに対してかなり高速な空間クエリが必要です。
ただし、私が見る限り、このデータ構造のアルゴリズムの説明には、必要なヒルベルト値を実際に計算する方法についても言及されていません。これは、ヒルベルト曲線に沿った点までの距離です。
では、これを計算する方法について何か提案はありますか?
楽しい質問!
私は少しグーグルをしました、そして良いニュースは、ヒルベルトバリューの実装を見つけたということです。
潜在的に悪いニュースは、Haskellにあるということです...
http://www.serpentine.com/blog/2007/01/11/two-dimension-spatial-hashing-with-space-filling-curves/
また、より簡単に計算できる可能性のあるルベーグ距離メトリックも提案します。
以下は、Software: Practice and Experience Vol. 26 pp 1335-46 (1996)。
お役に立てれば。改善歓迎!
マイケル
/**
* Find the Hilbert order (=vertex index) for the given grid cell
* coordinates.
* @param x cell column (from 0)
* @param y cell row (from 0)
* @param r resolution of Hilbert curve (grid will have Math.pow(2,r)
* rows and cols)
* @return Hilbert order
*/
public static int encode(int x, int y, int r) {
int mask = (1 << r) - 1;
int hodd = 0;
int heven = x ^ y;
int notx = ~x & mask;
int noty = ~y & mask;
int temp = notx ^ y;
int v0 = 0, v1 = 0;
for (int k = 1; k < r; k++) {
v1 = ((v1 & heven) | ((v0 ^ noty) & temp)) >> 1;
v0 = ((v0 & (v1 ^ notx)) | (~v0 & (v1 ^ noty))) >> 1;
}
hodd = (~v0 & (v1 ^ x)) | (v0 & (v1 ^ noty));
return interleaveBits(hodd, heven);
}
/**
* Interleave the bits from two input integer values
* @param odd integer holding bit values for odd bit positions
* @param even integer holding bit values for even bit positions
* @return the integer that results from interleaving the input bits
*
* @todo: I'm sure there's a more elegant way of doing this !
*/
private static int interleaveBits(int odd, int even) {
int val = 0;
// Replaced this line with the improved code provided by Tuska
// int n = Math.max(Integer.highestOneBit(odd), Integer.highestOneBit(even));
int max = Math.max(odd, even);
int n = 0;
while (max > 0) {
n++;
max >>= 1;
}
for (int i = 0; i < n; i++) {
int bitMask = 1 << i;
int a = (even & bitMask) > 0 ? (1 << (2*i)) : 0;
int b = (odd & bitMask) > 0 ? (1 << (2*i+1)) : 0;
val += a + b;
}
return val;
}
uzaygezenを参照してください。
上記のコードと Java コードは、2D データ ポイントに適しています。しかし、より高い次元については、Jonathan Lawder の論文である JKLawder を見る必要があるかもしれません。ヒルベルト空間充填曲線を使用した 1 次元値と n 次元値の間のマッピングの計算。
ビットをインターリーブするもう少し効率的な方法を見つけました。これは、Stanford Graphics の Web サイトにあります。2 つの 32 ビット整数を 1 つの 64 ビット長にインターリーブできるバージョンを作成しました。
public static long spreadBits32(int y) {
long[] B = new long[] {
0x5555555555555555L,
0x3333333333333333L,
0x0f0f0f0f0f0f0f0fL,
0x00ff00ff00ff00ffL,
0x0000ffff0000ffffL,
0x00000000ffffffffL
};
int[] S = new int[] { 1, 2, 4, 8, 16, 32 };
long x = y;
x = (x | (x << S[5])) & B[5];
x = (x | (x << S[4])) & B[4];
x = (x | (x << S[3])) & B[3];
x = (x | (x << S[2])) & B[2];
x = (x | (x << S[1])) & B[1];
x = (x | (x << S[0])) & B[0];
return x;
}
public static long interleave64(int x, int y) {
return spreadBits32(x) | (spreadBits32(y) << 1);
}
明らかに、BおよびSローカル変数はクラス定数である必要がありますが、簡単にするためにこのように残されています。
マイケル、
Javaコードをありがとう!テストしたところ、正常に動作しているようですが、「n」値はhighestOneBit( )-関数。上位1ビットの位置ではなく値を返します。したがって、ループは不必要に多くのインターリーブを実行します。
次のスニペットに変更したところ、正常に機能しました。
int max = Math.max(odd、even); int n = 0; while(max> 0){ n ++; 最大>>=1; }
高速な削除/挿入機能を備えた空間インデックスが必要な場合は、PH ツリーを参照してください。一部は四分木に基づいていますが、より高速でスペース効率が高くなります。内部的には、H カーブよりもわずかに悪い空間特性を持つ Z カーブを使用しますが、計算ははるかに簡単です。
論文: http://www.globis.ethz.ch/script/public/download?docid=699
Java 実装: http://globis.ethz.ch/files/2014/11/ph-tree-2014-11-10.zip
もう 1 つのオプションは X-tree です。こちらも利用できます: https://code.google.com/p/xxl/
提案: 空間クエリの単純で効率的な優れたデータ構造は、多次元バイナリ ツリーです。
従来の二分木には、「判別式」が 1 つあります。左の分岐を取るか右の分岐を取るかを決定するために使用される値。これは 1 次元のケースと見なすことができます。
多次元二分木では、複数の判別式があります。連続するレベルでは、異なる判別式が使用されます。たとえば、2 次元の空間データの場合、X 座標と Y 座標を判別式として使用できます。連続するレベルでは、X、Y、X、Y... を使用します。
空間クエリ (たとえば、四角形内のすべてのノードを見つける) の場合、ルートから開始してツリーの深さ優先検索を実行し、各レベルで判別式を使用して、指定された四角形内にノードを含まないブランチを検索しないようにします。
これにより、各レベルで検索スペースを半分に削減できる可能性があり、大量のデータ セットで小さな領域を見つけるのに非常に効率的になります。(ちなみに、このデータ構造は部分一致クエリ、つまり 1 つ以上の判別式を省略したクエリにも役立ちます。判別式を省略したレベルで両方のブランチを検索するだけです。)
このデータ構造に関する優れた論文: http://portal.acm.org/citation.cfm?id=361007 この記事には、優れた図とアルゴリズムの説明があります: http://en.wikipedia.org/wiki/Kd-tree