Функція спарювання Cantor - це дійсно одна з найкращих ситуацій там, враховуючи її простий, швидкий та просторовий, але тут ще більше опубліковано в Wolfram Матвія Шудзіка . Обмеження функції спарювання Кантора (відносно) полягає в тому, що діапазон кодованих результатів не завжди залишається в межах 2N
бітового цілого числа, якщо входи - це N
двобітні цілі числа. Тобто, якщо мої входи - це два 16
бітові цілі числа, починаючи з 0 to 2^16 -1
, тоді можливі 2^16 * (2^16 -1)
комбінації входів, тож за очевидним принципом Pigeonhole нам потрібен вихід розміру принаймні, як бітові числа. Це може бути не мало практичного значення у світі програмування.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 біти.
Введіть функцію Szudzik :
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-бітного цілого числа.
Тепер функція Szudzik :
(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-бітових входів може бути настільки непростимим !!
Функція Szudzik :
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-бітним для діапазону підписаних, але все ж краще.
Тепер все це, хоча результат завжди був позитивним. У світі, що підписався, буде ще більше економії місця, якщо ми зможемо перенести половину виведення на негативну вісь . Ви можете зробити це так, як для Szudzik:
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
бітового цілого числа, яке є крутим. Я не впевнений, як пройти такий самий шлях для функції з’єднання Cantor, але не намагався настільки, наскільки це не настільки ефективно. Крім того, більше обчислень, що беруть участь у функції сполучення Кантора, означає і її повільність .
Ось реалізація 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
).
Я надав посилання на альтернативне рішення, добре зображує графік функції, використовуючи кожну точку простору. Дивно бачити, що ви могли однозначно кодувати пару координат на одне число зворотно! Чарівний світ чисел !!