实现互联网的希尔伯特地图

时间:2009-12-29 20:45:53

标签: algorithm math hilbert-curve

XKCD comic 195中,使用Hilbert curve建议了互联网地址空间地图的设计,以便来自类似IP地址的项目将聚集在一起。

给定一个IP地址,如何在这样的地图上计算其2D坐标(在0到1的范围内)?

3 个答案:

答案 0 :(得分:14)

这非常简单,因为希尔伯特曲线是分形的,也就是说,它是递归的。它通过水平和垂直平分每个方块,将其分成四个部分来工作。因此,您从左侧开始一次取两位IP地址,并使用它们来确定象限,然后继续使用接下来的两位,使用该象限而不是整个方块,依此类推,直到您拥有耗尽了地址中的所有位。

每个方格的曲线的基本形状是马蹄形的:

 0 3
 1 2

其中数字对应于前两位,因此确定遍历顺序。在xkcd地图中,此正方形是最高级别的遍历顺序。可能旋转和/或反射,这种形状出现在每个2x2的正方形。

确定“马蹄铁”在每个子方格中的定向方式取决于一条规则:0方的0角位于较大方块的角落。因此,必须按顺序遍历与0对应的子方格

 0 1
 3 2

并且,查看整个前一个方块并显示四位,我们得到以下形状用于广场的下一个分区:

 00 01 32 33
 03 02 31 30
 10 13 20 23
 11 12 21 22

这就是广场总是在下一个级别划分的方式。现在,继续,只关注后两位,根据这些位的马蹄形状如何定向,定义更详细的形状,并继续进行类似的划分。

为了确定实际坐标,每两位确定实数坐标中的一位二进制精度。因此,在第一级,如果地址的前两位具有,则[0,1]坐标中的二进制点之后的第一位(假设x范围内的坐标)为001,否则为1。同样,如果前两位的值为y0,则1坐标中的第一位为2。要确定是否在坐标中添加01位,您需要检查该级别的马蹄形方向。

编辑:我开始计算算法,事实证明它毕竟不是那么难,所以这里有一些伪C。它是伪的,因为我对二进制常量使用b后缀并将整数视为位数组,但将其更改为正确的C应该不会太难。

在代码中,pos是方向的3位整数。前两位是方形中0的x和y坐标,第三位指示1是否具有与0相同的x坐标。 pos的初始值为011b,表示0的坐标为(0, 1)1的坐标与0具有相同的x坐标。 ad是地址,被视为2位整数的n - 元素数组,从最高位开始。

double x = 0.0, y = 0.0;
double xinc, yinc;
pos = 011b;
for (int i = 0; i < n; i++) {
    switch (ad[i]) {
        case 0: xinc = pos[0]; yinc = pos[1]; pos[2] = ~pos[2]; break;
        case 1: xinc = pos[0] ^ ~pos[2]; yinc = pos[1] ^ pos[2]; break;
        case 2: xinc = ~pos[0]; yinc = ~pos[1]; break;
        case 3: xinc = pos[0] ^ pos[2]; yinc = pos[1] ^ ~pos[2];
            pos = ~pos; break;
    }
    x += xinc / (1 << (i+1)); y += yinc / (1 << (i+1));
}

我用几个8位前缀对它进行了测试,并根据xkcd地图正确放置它们,所以我有点相信代码是正确的。

答案 1 :(得分:5)

基本上你会使用位对,MSB到LSB来分解数字。这对位告诉您位置是在左上(0)左下(1)右下(2)还是右上(3)象限,当您在数字中移动时,该比例变得更精细。

此外,您需要跟踪“方向”。这是用于您所在规模的绕组;初始绕组如上(UL,LL,LR,UR),并且根据您最终进入的象限,下一个缩小的绕组是(当前绕组旋转-90,0,0,+ 90)

所以你可以积累抵消:

假设我从0,0开始,第一对给我一个2,我将偏移量偏移到0.5,0.5。右下方的绕组与我的初始绕组相同。下一对减小了比例,因此我的调整长度为0.25。

这对是3,所以我只翻译我的x坐标,我是.75,.5。绕组现在旋转,我的下一个缩小将是(LR,LL,UL,UR)。现在规模是.125,依此类推,直到我的地址用完为止。

答案 2 :(得分:0)

我希望根据wikipedia code for a Hilbert curve,您可以跟踪当前位置(作为(x,y)坐标)并在访问n个单元格后返回该位置。那么缩放到[0..1]的位置将取决于Hilbert曲线在完成时的高度和宽度。

from turtle import left, right, forward

size = 10

def hilbert(level, angle):
    if level:
        right(angle)
        hilbert(level - 1, -angle)
        forward(size)
        left(angle)
        hilbert(level - 1, angle)
        forward(size)
        hilbert(level - 1, angle)
        left(angle)
        forward(size)
        hilbert(level - 1, -angle)
        right(angle)

不可否认,这将是一个强力解决方案,而不是封闭形式的解决方案。