Cantorペアリング関数は、シンプルで高速でスペース効率が良いことを考えると、実際には優れた関数の1つですが、WolframでMatthew Szudzikによって公開されているさらに優れた関数があります。2N
Cantor ペアリング関数の (相対的な) 制限は、入力が 2 ビット整数の場合、エンコードされた結果の範囲が常にビット整数の制限内に留まらないことN
です。つまり、私の入力が16
からの範囲の 2 ビット整数である場合、可能な入力の組み合わせ0 to 2^16 -1
が2^16 * (2^16 -1)
あるため、明白なピジョンホールの原理により、少なくとも2^16 * (2^16 -1)
に等しいサイズの出力が必要です。2^32 - 2^16
つまり、 のマップです。32
ビット数は理想的には実行可能でなければなりません。これは、プログラミングの世界では実際にはほとんど重要ではないかもしれません。
Cantor ペアリング機能:
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
2 つの最大 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
。
Cantor ペアリング機能:
2 つの最大 16 ビット符号付き整数 (32767、32767) のマッピングは 2147418112 になり、符号付き 32 ビット整数の最大値をわずかに下回ります。
今シュジクの関数:
(32767, 32767) => 1073741823、はるかに小さい..
負の整数を考慮しましょう。それは私が知っている元の質問を超えていますが、将来の訪問者を助けるために詳しく説明しています.
Cantor ペアリング機能:
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 です。16 ビット入力に対して 64 ビット出力は、とても許せないかもしれません!!
シュジク関数:
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
入力に の重みを適用し、関数を実行した後、出力を 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
)。
代替ソリューションで提供したリンクは、空間内のすべての点を利用した関数のグラフをうまく表しています。1 組の座標を 1 つの数値に可逆的に一意にエンコードできることは驚くべきことです。数字の魔法の世界!!