Cantor eşleştirme işlevi , basit, hızlı ve alan açısından verimli olduğu düşünüldüğünde gerçekten daha iyi olanlardan biridir, ancak Wolfram'da Matthew Szudzik tarafından daha iyi yayınlanan bir şey var . Cantor eşleştirme işlevinin (göreceli olarak) sınırlanması 2N
, girişler iki N
bit tamsayı ise, kodlanmış sonuç aralığının her zaman bir bit tamsayısının sınırları içinde kalmamasıdır . Yani, eğer girişlerim iki 16
bitlik tamsayılar arasındaysa 0 to 2^16 -1
, o zaman 2^16 * (2^16 -1)
olası giriş kombinasyonları vardır , bu yüzden bariz Pigeonhole Prensibi ile , en azından 2^16 * (2^16 -1)
, yani 2^32 - 2^16
bir haritaya eşit veya başka bir deyişle,32
bit sayıları ideal olarak mümkün olmalıdır. Programlama dünyasında bu pek pratik bir öneme sahip olmayabilir.
Cantor eşleştirme işlevi :
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
İki maksimum en fazla 16 bit tam sayı (65535, 65535) için eşleme, gördüğünüz gibi 32 bite sığamayacak 8589803520 olacaktır.
Szudzik işlevini girin :
a >= b ? a * a + a + b : a + b * b; where a, b >= 0
(65535, 65535) için eşleme şimdi gördüğünüz gibi 32 bit (0 ila 2 ^ 32 -1) tam sayı olan 4294967295 olacaktır. Burası bu çözümün ideal olduğu yerdir, o alandaki her noktayı kullanır, böylece hiçbir şey daha fazla yer tasarrufu sağlayamaz.
Şimdi, tipik olarak, diller / çerçevelerdeki çeşitli boyutlarda sayıların imzalı uygulamalarını ele aldığımızı göz önünde bulundurarak, aralarındaki signed 16
bit tamsayılarını ele alalım -(2^15) to 2^15 -1
(daha sonra çıkışın bile işaretli aralığa yayılmasını nasıl uzatacağımızı göreceğiz). O zamandan beri a
ve b
pozitif olmak zorundalar 0 to 2^15 - 1
.
Cantor eşleştirme işlevi :
İki en çok 16 bit işaretli tam sayı (32767, 32767) için eşleme 2147418112 olacak ve işaretli 32 bit tam sayı için maksimum değerin sadece kısa.
Şimdi Szudzik'in işlevi :
(32767, 32767) => 1073741823, çok daha küçük ..
Negatif tamsayıları açıklayalım. Bu bildiğim orijinal sorunun ötesinde, ancak gelecekteki ziyaretçilere yardımcı olmak için ayrıntılı.
Cantor eşleştirme işlevi :
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 olan Int64. 16 bit girişler için 64 bit çıkış çok affedilmez olabilir !!
Szudzik'in işlevi :
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, imzasız aralık için 32 bit veya işaretli aralık için 64 bit, ancak yine de daha iyi.
Şimdi tüm bunlar çıktı her zaman pozitif olmuştur. İmzalı dünyada, çıktının yarısını negatif eksene aktarabilirsek daha fazla yer tasarrufu sağlayacaktır . Szudzik's için şu şekilde yapabilirsiniz:
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
Yaptığım şey: 2
Girdilere bir ağırlık uyguladıktan ve işlevden geçtikten sonra, çıkışı ikiye bölerim ve bir kısmını çarparak negatif eksene götürürüm -1
.
Sonuçlara bakın, işaretli bir 16
bit sayısı aralığındaki herhangi bir girdi için , çıktı 32
, serin olan işaretli bir bit tamsayısının sınırları içinde yer alır . Nasıl Cantor eşleştirme işlevi için aynı şekilde gitmek emin değilim ama onun kadar etkili değil kadar denemedim. Ayrıca, Cantor eşleştirme işlevinde daha fazla hesaplama yapılması onun daha yavaş olduğu anlamına gelir .
İşte bir C # uygulaması.
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;
}
Ara hesaplamalar 2N
işaretli tamsayı sınırlarını aşabileceğinden , 4N
tamsayı türünü kullandım (son bölüme 2
sonucu geri getirir 2N
).
Alternatif çözüm üzerinde sağladığım bağlantı, uzayda her bir noktayı kullanan fonksiyonun bir grafiğini güzel bir şekilde tasvir ediyor. Bir çift koordinatı geri dönüşümlü olarak tek bir sayıya benzersiz bir şekilde kodlayabileceğinizi görmek şaşırtıcı! Sayıların sihirli dünyası !!