Функция сопряжения Cantor действительно одна из лучших, учитывая ее простоту, скорость и компактность, но есть кое-что еще лучше, опубликованное в Wolfram Мэтью Шудзиком здесь. Ограничение функции сопряжения Кантора (относительно) состоит в том, что диапазон закодированных результатов не всегда остается в пределах 2N
-битового целого числа, если входные данные представляют собой два N
-битовых целых числа. То есть, если мои входные данные представляют собой два 16
битовых целых числа в диапазоне от 0 to 2^16 -1
, тогда возможны 2^16 * (2^16 -1)
комбинаций входных данных, поэтому из очевидного Принцип голубятни, нам нужен результат размером не менее 2^16 * (2^16 -1)
, который равен 2^32 - 2^16
, или, другими словами, в идеале должна быть осуществима карта 32
битовых чисел. Это может иметь немаловажное практическое значение в мире программирования.
Функция сопряжения Кантора:
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
Отображение для двух максимальных 16-битных целых чисел (65535, 65535) будет 8589803520, что, как вы видите, не может уместиться в 32-битные.
Введите функцию Судзика:
a >= b ? a * a + a + b : a + b * b; where a, b >= 0
Отображение для (65535, 65535) теперь будет 4294967295, что, как вы видите, является 32-битным (от 0 до 2 ^ 32 -1) целым числом. Вот где это решение идеально, оно просто использует каждую точку в этом пространстве, поэтому ничто не может получить более эффективное использование пространства.
Теперь, учитывая тот факт, что мы обычно имеем дело с подписанными реализациями чисел различного размера в языках / фреймворках, давайте рассмотрим signed 16
битовые целые числа в диапазоне от -(2^15) to 2^15 -1
(позже мы увидим, как расширить даже вывод для охвата подписанного диапазона). Поскольку a
и b
должны быть положительными, они варьируются от 0 to 2^15 - 1
.
Функция сопряжения Кантора:
Отображение для двух максимальных 16-битных целых чисел со знаком (32767, 32767) будет 2147418112, что немного меньше максимального значения для 32-битного целого числа со знаком.
Теперь функция Судзика:
(32767, 32767) => 1073741823, намного меньше ..
Учтем отрицательные целые числа. Это выходит за рамки исходного вопроса, который я знаю, но просто уточняю, чтобы помочь будущим посетителям.
Функция сопряжения Кантора:
A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
(A + B) * (A + B + 1) / 2 + A;
(-32768, -32768) => 8589803520, что является Int64. 64-битный вывод для 16-битных входов может быть таким непростительным !!
Функция Судзика:
A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
A >= B ? A * A + A + B : A + B * B;
(-32768, -32768) => 4294967295, что является 32-битным для беззнакового диапазона или 64-битным для подписанного диапазона, но все же лучше.
Теперь при всем при этом выход всегда был положительным. В мире со знаком было бы еще больше экономии места, если бы мы могли перенести половину вывода на отрицательную ось. Для Шудзика это можно сделать так:
A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
C = (A >= B ? A * A + A + B : A + B * B) / 2;
a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
(-32768, 32767) => -2147483648
(32767, -32768) => -2147450880
(0, 0) => 0
(32767, 32767) => 2147418112
(-32768, -32768) => 2147483647
Что я делаю: после применения веса 2
к входам и прохождения функции, я затем делю выходной сигнал на два и переношу некоторые из них на отрицательную ось, умножая на -1
.
Посмотрите результаты: для любого входа в диапазоне 16
-битного числа со знаком выход находится в пределах 32
-битового целого числа со знаком, что неплохо. Я не уверен, как сделать то же самое для функции сопряжения Кантора, но не пробовал так много, как это не так эффективно. Кроме того, чем больше вычислений выполняется функция связывания Кантора, тем медленнее она тоже.
Вот реализация на C #.
public static long PerfectlyHashThem(int a, int b)
{
var A = (ulong)(a >= 0 ? 2 * (long)a : -2 * (long)a - 1);
var B = (ulong)(b >= 0 ? 2 * (long)b : -2 * (long)b - 1);
var C = (long)((A >= B ? A * A + A + B : A + B * B) / 2);
return a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
}
public static int PerfectlyHashThem(short a, short b)
{
var A = (uint)(a >= 0 ? 2 * a : -2 * a - 1);
var B = (uint)(b >= 0 ? 2 * b : -2 * b - 1);
var C = (int)((A >= B ? A * A + A + B : A + B * B) / 2);
return a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
}
Поскольку промежуточные вычисления могут выходить за пределы 2N
целого числа со знаком, я использовал 4N
целочисленный тип (последнее деление на 2
возвращает результат к 2N
).
Ссылка, которую я предоставил на альтернативное решение, красиво изображает график функции, использующий каждую отдельную точку в пространстве. Удивительно видеть, что вы можете однозначно закодировать пару координат в одно число обратимо! Волшебный мир чисел !!
person
nawfal
schedule
14.12.2012
10,001*A + B
? - person BlueRaja - Danny Pflughoeft   schedule 13.07.2011