Chức năng ghép nối Cantor thực sự là một trong những chức năng tốt hơn ngoài kia vì nó đơn giản, nhanh và hiệu quả về không gian, nhưng có một thứ thậm chí còn được xuất bản tốt hơn tại Wolfram của Matthew Szudzik, ở đây . Hạn chế của chức năng ghép cặp Cantor (tương đối) là phạm vi kết quả được mã hóa không phải luôn nằm trong giới hạn của một 2N
số nguyên bit nếu đầu vào là hai N
số nguyên bit. Đó là, nếu đầu vào của tôi là hai 16
số nguyên bit khác nhau 0 to 2^16 -1
, thì có thể 2^16 * (2^16 -1)
kết hợp các đầu vào, do đó, theo Nguyên tắc Pigeonhole rõ ràng , chúng ta cần một đầu ra có kích thước tối thiểu 2^16 * (2^16 -1)
, bằng 2^32 - 2^16
, hay nói cách khác là bản đồ32
số bit nên khả thi lý tưởng. Điều này có thể không có ít tầm quan trọng thực tế trong thế giới lập trình.
Chức năng ghép cặp :
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
Ánh xạ cho hai số nguyên tối đa 16 bit (65535, 65535) sẽ là 8589803520 mà bạn thấy không thể phù hợp với 32 bit.
Nhập chức năng của Szudzik :
a >= b ? a * a + a + b : a + b * b; where a, b >= 0
Ánh xạ cho (65535, 65535) bây giờ sẽ là 4294967295 mà bạn thấy là số nguyên 32 bit (0 đến 2 ^ 32 -1). Đây là nơi giải pháp này là lý tưởng, nó chỉ đơn giản là sử dụng mọi điểm duy nhất trong không gian đó, vì vậy không có gì có thể có được không gian hiệu quả hơn.
Bây giờ xem xét thực tế là chúng ta thường xử lý các triển khai đã ký của các số có kích cỡ khác nhau trong các ngôn ngữ / khung, hãy xem xét signed 16
các số nguyên bit khác nhau -(2^15) to 2^15 -1
(sau này chúng ta sẽ xem làm thế nào để mở rộng ngay cả phạm vi để vượt qua phạm vi đã ký). Vì a
và b
phải tích cực họ phạm vi từ 0 to 2^15 - 1
.
Chức năng ghép cặp :
Ánh xạ cho hai số nguyên được ký tối đa 16 bit (32767, 32767) sẽ là 2147418112, chỉ thiếu giá trị tối đa cho số nguyên 32 bit đã ký.
Bây giờ chức năng của Szudzik :
(32767, 32767) => 1073741823, nhỏ hơn nhiều ..
Hãy để tài khoản cho số nguyên âm. Đó là ngoài câu hỏi ban đầu tôi biết, nhưng chỉ cần xây dựng để giúp khách truy cập trong tương lai.
Chức năng ghép cặp :
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 là Int64. Đầu ra 64 bit cho đầu vào 16 bit có thể không được chấp nhận !!
Chức năng của 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 là 32 bit cho phạm vi không dấu hoặc 64 bit cho phạm vi đã ký, nhưng vẫn tốt hơn.
Bây giờ tất cả điều này trong khi đầu ra luôn luôn tích cực. Trong thế giới đã ký, sẽ còn tiết kiệm không gian hơn nữa nếu chúng ta có thể chuyển một nửa sản lượng sang trục âm . Bạn có thể làm điều đó như thế này cho 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
Những gì tôi làm: Sau khi áp dụng trọng số của 2
các đầu vào và đi qua hàm, sau đó tôi chia số dư cho hai và lấy một số trong số chúng cho trục âm bằng cách nhân với -1
.
Xem kết quả, đối với bất kỳ đầu vào nào trong phạm vi số 16
bit đã ký , đầu ra nằm trong giới hạn của 32
số nguyên bit đã ký là tuyệt vời. Tôi không chắc chắn cách thực hiện theo cách tương tự cho chức năng ghép nối Cantor nhưng không thử nhiều như nó không hiệu quả. Hơn nữa, nhiều tính toán liên quan đến chức năng ghép cặp Cantor cũng có nghĩa là nó cũng chậm hơn .
Đây là một triển khai 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;
}
Vì các tính toán trung gian có thể vượt quá giới hạn của 2N
số nguyên đã ký, tôi đã sử dụng 4N
loại số nguyên (phép chia cuối cùng bằng cách 2
đưa kết quả trở lại 2N
).
Liên kết tôi đã cung cấp trên giải pháp thay thế mô tả độc đáo một biểu đồ của hàm sử dụng mọi điểm duy nhất trong không gian. Thật tuyệt vời khi thấy rằng bạn có thể mã hóa duy nhất một cặp tọa độ thành một số duy nhất có thể đảo ngược! Thế giới kỳ diệu của những con số !!