计算要在希尔伯特R树中使用的点的希尔伯特值?
我有一个应用程序,其中希尔伯特R树(Wikipedia)(引文)似乎是合适的数据结构。具体来说,它要求对数据集进行相当快的空间查询,这将经历很多更新。
但是,据我所知,该数据结构的算法描述都没有提到如何实际计算所需的希尔伯特值。这是沿着希尔伯特曲线到该点的距离。
那么关于如何进行计算的任何建议呢?
解决方案
好玩的问题!
我做了一些谷歌搜索,好消息是,我找到了希尔伯特·费特(Hilbert Value)的实现。
潜在的坏消息是,它在Haskell中。
http://www.serpentine.com/blog/2007/01/11/two-dimensional-spatial-hashing-with-space-filling-curves/
它还提出了一个Lebesgue距离度量标准,我们也许可以更轻松地进行计算。
参见uzaygezen。
建议:一个用于空间查询的简单高效的数据结构是多维二叉树。
在传统的二叉树中,有一个"判别式"。用于确定我们采用左分支还是右分支的值。可以认为是一维情况。
在多维二叉树中,我们有多个判别式。连续级别使用不同的判别式。例如,对于二维空间数据,可以将X和Y坐标用作判别式。连续级别将使用X,Y,X,Y ...
对于空间查询(例如查找矩形内的所有节点),我们将从树的根部开始对树进行深度优先搜索,并在每个级别上使用判别式,以避免向下搜索给定矩形中不包含节点的分支。
这样一来,我们可以将每个级别的搜索空间减少一半,从而非常有效地在海量数据集中查找较小的区域。 (顺便说一句,此数据结构对于部分匹配查询(即省略一个或者多个判别式的查询也很有用。我们只需在两个分支中以被忽略的判别式向下搜索)。)
关于此数据结构的好论文:http://portal.acm.org/citation.cfm?id=361007
本文提供了很好的图表和算法说明:http://en.wikipedia.org/wiki/Kd-tree
以下是我的Java代码,改编自Xian Lu和Gunther Schrack发表于Software:Practice and Experience Vol。的"编码和解码Hilbert顺序"一文中的C代码。 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; }
迈克尔
感谢Java代码!我对其进行了测试,但似乎工作正常,但我注意到位交织函数在递归级别7上溢出(至少在我的测试中,但我使用的是长值),因为" n"值是使用highestOneBit( )函数,它返回值而不是最高一位的位置;因此循环不必要地进行了许多交错。
我只是将其更改为以下代码段,然后运行良好。
int max = Math.max(odd, even); int n = 0; while (max > 0) { n++; max >>= 1; }
我想出了一种更有效的交织位的方法。可以在Stanford Graphics网站上找到它。我提供了一个我创建的版本,该版本可以将两个32位整数交错为一个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代码适用于2D数据点。但是对于更大的尺寸,我们可能需要查看乔纳森·劳德(Jonathan Lawder)的论文:J.K。Lawder。使用希尔伯特空间填充曲线计算一维和n维值之间的映射。