Die Cantor-Pairing-Funktion ist wirklich eine der besseren, wenn man bedenkt, dass sie einfach, schnell und platzsparend ist, aber hier gibt es etwas noch Besseres, das Matthew Szudzik bei Wolfram veröffentlicht hat . Die Einschränkung der Cantor-Pairing-Funktion (relativ) besteht darin, dass der Bereich der codierten Ergebnisse nicht immer innerhalb der Grenzen einer 2N
Bit-Ganzzahl bleibt, wenn die Eingaben zwei N
Bit-Ganzzahlen sind. Das heißt, wenn meine Eingaben zwei 16
Bit-Ganzzahlen im Bereich von sind 0 to 2^16 -1
, sind 2^16 * (2^16 -1)
Kombinationen von Eingaben möglich. Nach dem offensichtlichen Pigeonhole-Prinzip benötigen wir also mindestens eine Ausgabe mit einer Größe 2^16 * (2^16 -1)
, die gleich 2^32 - 2^16
oder mit anderen Worten einer Karte von ist32
Bitnummern sollten idealerweise machbar sein. Dies ist in der Programmierwelt möglicherweise nicht von geringer praktischer Bedeutung.
Cantor-Pairing-Funktion :
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
Die Zuordnung für zwei maximal 16-Bit-Ganzzahlen (65535, 65535) lautet 8589803520, was, wie Sie sehen, nicht in 32-Bit passt.
Geben Sie die Funktion von Szudzik ein :
a >= b ? a * a + a + b : a + b * b; where a, b >= 0
Die Zuordnung für (65535, 65535) lautet jetzt 4294967295, was, wie Sie sehen, eine 32-Bit-Ganzzahl (0 bis 2 ^ 32 -1) ist. Hier ist diese Lösung ideal. Sie nutzt einfach jeden einzelnen Punkt in diesem Raum, sodass nichts platzsparender werden kann.
Angesichts der Tatsache, dass wir uns normalerweise mit den signierten Implementierungen von Zahlen unterschiedlicher Größe in Sprachen / Frameworks befassen, betrachten wir signed 16
Bit-Ganzzahlen im Bereich von -(2^15) to 2^15 -1
(später werden wir sehen, wie sogar die Ausgabe auf den signierten Bereich ausgedehnt werden kann). Da a
und b
müssen positiv sein, reichen sie von 0 to 2^15 - 1
.
Cantor-Pairing-Funktion :
Die Zuordnung für zwei maximal 16-Bit-Ganzzahlen mit Vorzeichen (32767, 32767) lautet 2147418112, was knapp unter dem Maximalwert für vorzeichenbehaftete 32-Bit-Ganzzahlen liegt.
Nun Szudziks Funktion :
(32767, 32767) => 1073741823, viel kleiner.
Lassen Sie uns negative ganze Zahlen berücksichtigen. Das geht über die ursprüngliche Frage hinaus, die ich kenne, aber ich arbeite nur daran, zukünftigen Besuchern zu helfen.
Cantor-Pairing-Funktion :
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, das ist Int64. 64-Bit-Ausgabe für 16-Bit-Eingänge kann so unverzeihlich sein !!
Szudziks Funktion :
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 Bit für vorzeichenlosen Bereich oder 64 Bit für vorzeichenbehafteten Bereich, aber immer noch besser.
Jetzt alles, während die Ausgabe immer positiv war. In der signierten Welt wird es noch platzsparender sein, wenn wir die Hälfte der Ausgabe auf die negative Achse übertragen könnten . Sie könnten es für Szudzik so machen:
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
Was ich tue: Nachdem ich 2
den Eingaben ein Gewicht von zugewiesen und die Funktion durchlaufen habe, teile ich den Ausgang durch zwei und bringe einige von ihnen durch Multiplizieren mit auf die negative Achse -1
.
Siehe die Ergebnisse. Für jede Eingabe im Bereich einer vorzeichenbehafteten 16
Bitnummer liegt die Ausgabe innerhalb der Grenzen einer vorzeichenbehafteten 32
Bit-Ganzzahl, die cool ist. Ich bin mir nicht sicher, wie ich den gleichen Weg für die Cantor-Pairing-Funktion gehen soll, habe aber nicht so viel versucht, wie es nicht so effizient ist. Darüber hinaus bedeutet mehr Berechnungen in der Cantor-Pairing-Funktion, dass sie auch langsamer ist .
Hier ist eine C # -Implementierung.
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;
}
Da die Zwischenberechnungen die Grenzen der 2N
vorzeichenbehafteten Ganzzahl überschreiten können , habe ich den 4N
Ganzzahltyp verwendet (die letzte Division durch 2
bringt das Ergebnis zurück zu 2N
).
Der Link, den ich für eine alternative Lösung bereitgestellt habe, zeigt eine grafische Darstellung der Funktion, die jeden einzelnen Punkt im Raum nutzt. Es ist erstaunlich zu sehen, dass Sie ein Koordinatenpaar eindeutig reversibel in eine einzelne Zahl codieren können! Magische Welt der Zahlen !!