Ответ 1
Это довольно легко, так как кривая Гильберта является фрактальной, то есть рекурсивной. Он работает, разбивая каждый квадрат по горизонтали и вертикали, разделяя его на четыре части. Таким образом, вы берете два бита 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
равен 0
, если первые два бита адреса имеют значение 0
или 1
и 1
в противном случае. Аналогично, первый бит в координате y
равен 0
, если первые два бита имеют значение 1
или 2
. Чтобы определить, следует ли добавить бит 0
или 1
в координаты, вам необходимо проверить ориентацию подков на этом уровне.
EDIT: я начал разрабатывать алгоритм, и получается, что это не так сложно в конце концов, так что здесь некоторые псевдо-C. Это псевдо, потому что я использую суффикс b
для двоичных констант и обрабатываю целые числа как массивы бит, но изменение его на правильное С не должно быть слишком сложным.
В коде pos
представляет собой 3-битное целое число для ориентации. Первые два бита представляют собой координаты x и y 0
в квадрате, а третий бит указывает, имеет ли 1
ту же координату x, что и 0
. Начальное значение pos
составляет 011b
, что означает, что координаты 0
равны (0, 1)
, а 1
имеет ту же координату x, что и 0
. ad
- это адрес, рассматриваемый как массив n
-элемент из 2-битных целых чисел и начинающийся с наиболее значимых бит.
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, поэтому я уверен, что код верен.