Karma işlevleri neden asal sayı modülü kullanmalı?


336

Uzun zaman önce, pazarlık masasından bir veri yapıları kitabı 1,25 dolara satın aldım. İçinde, bir karma işlevinin açıklaması, "matematiğin doğası" nedeniyle sonuçta asal bir sayı ile değişmesi gerektiğini söyledi.

1,25 $ 'lık bir kitaptan ne bekliyorsunuz?

Her neyse, matematiğin doğasını düşünmek için yıllar geçirdim ve hala anlayamıyorum.

Çok sayıda kova olsa bile sayıların dağılımı gerçekten daha mı fazla? Ya da bu herkesin çünkü herkes kabul ettiğini yaşlı bir programcının masalı başka bunu kabul eder?


1
Mükemmel mantıklı soru: Neden asal sayıda kova olmalı?
Draemon

1
Bu soru konu dışı gibi görünmektedir çünkü muhtemelen Bilgisayar Bilimi'ne aittir .
Orbit'te Hafiflik Yarışları

2
cs.stackexchange.com/a/64191/64222 bir başka iyi tartışılmış açıklama.
Yeşil Ağaç


İşte bazı şaşırtıcı kanıt numaraları ile biraz ilgili bir soru için başka bir büyük açıklama - quora.com/…
AnBisw

Yanıtlar:


242

Genellikle basit bir karma işlevi, girdinin "bileşen parçalarını" (bir dize durumunda karakterler) alıp bunları bir sabitin güçleriyle çarparak ve bunları bir tamsayı türünde bir araya getirerek çalışır. Örneğin, bir dizenin tipik bir (özellikle iyi olmasa da) karması şöyle olabilir:

(first char) + k * (second char) + k^2 * (third char) + ...

Daha sonra, aynı ilk karaktere sahip bir dizi dizge beslenirse, sonuçların tümü, en azından tamsayı tipi taşana kadar aynı modulo k olacaktır.

[Örnek olarak, Java'nın hashCode dizesi buna benzerdir - karakterleri ters sırayla yapar, k = 31 ile. Böylece, aynı şekilde biten dizeler arasında çarpıcı ilişkiler modulo 31 ve sonuna yakın dışında aynı olan dizeler arasında çarpıcı modulo 2 ^ 32 elde edersiniz. Bu, hashtable davranışı ciddiye almaz.]

Bir karma tablo, karma sayısının kova sayısına göre alınmasıyla çalışır.

Çarpışmalar, hashtable'ın verimliliğini azalttığından, olası durumlar için çarpışma üretmemek önemlidir.

Şimdi, birisinin, tüm değerler aynı ilk karaktere sahip olduğu gibi, öğeler arasında bir ilişkisi olan bir hashtable'a bir sürü değer koyduğunu varsayalım. Bu oldukça tahmin edilebilir bir kullanım modeli, diyebilirim, bu yüzden çok fazla çarpışma üretmesini istemiyoruz.

"Matematiğin doğası nedeniyle", eğer karmada kullanılan sabit ve kova sayısının eşzamanlı olduğu ortaya çıkarsa , bazı yaygın durumlarda çarpışmalar en aza indirilir. Eğer onlar bir eştir değilse, çarpışmaların en aza indirilmediği girdiler arasında oldukça basit bazı ilişkiler vardır. Tüm karmalar ortak faktöre eşit modulo çıkar, bu da hepsinin ortak faktörü olan bu modulo değerine sahip kovaların 1 / n'ine düşeceği anlamına gelir. N çarpı çarpma elde edersiniz, burada n ortak faktördür. N en az 2 olduğu için, normalden en az iki kat daha fazla çarpışma oluşturmanın oldukça basit bir kullanım durumu için kabul edilemez olduğunu söyleyebilirim. Bazı kullanıcılar dağıtımımızı bölümlere ayıracaksa, bunun basit bir öngörülebilir kullanım değil, bir ucube kazası olmasını istiyoruz.

Şimdi, hashtable uygulamaların kendilerine konulan öğeler üzerinde hiçbir kontrolü yoktur. İlişkilerini engelleyemezler. Dolayısıyla yapılacak şey, sabit ve kepçe sayımlarının eş zamanlı olmasını sağlamaktır. Bu şekilde, kepçenin bazı küçük ortak faktörlere göre modülünü belirlemek için yalnızca "son" bileşene güvenmezsiniz. Bildiğim kadarıyla bunu başarmak için asal olmak zorunda değiller, sadece coprime.

Ancak, hash işlevi ve hashtable bağımsız olarak yazılırsa, hashtable, hash işlevinin nasıl çalıştığını bilmez. Küçük faktörlere sahip bir sabit kullanıyor olabilir. Şanslıysanız, tamamen farklı çalışabilir ve doğrusal olmayabilir. Karma yeterince iyiyse, herhangi bir kova sayısı gayet iyidir. Ancak paranoyak bir hashtable iyi bir hash fonksiyonu üstlenemez, bu nedenle asal sayıda kova kullanmalıdır. Benzer şekilde, bir paranoyak hash fonksiyonu, birinin sabit ile ortak bir faktöre sahip olan bir dizi kova kullanma şansını azaltmak için büyük bir ana sabit kullanmalıdır.

Uygulamada, kova sayısı olarak 2'lik bir güç kullanmanın oldukça normal olduğunu düşünüyorum. Bu, kullanışlıdır ve doğru büyüklükte asal sayıda arama yapmak veya önceden seçmek zorunda kalmadan kaydeder. Bu nedenle, genellikle güvenli bir varsayım olan çarpanları kullanmamak için karma işlevine güvenirsiniz. Ancak, yukarıdaki gibi karma işlevlere dayalı olarak zaman zaman kötü karma davranışlar elde edebilirsiniz ve asal kova sayısı daha fazla yardımcı olabilir.

"Her şeyin asıl olması gerektiği" ilkesini benimsediğim kadarıyla hashtable'lar üzerinde iyi dağıtım için yeterli ancak gerekli bir koşul değil. Herkesin, diğerlerinin aynı kuralı izlediğini varsaymaya gerek kalmadan birlikte çalışmasına izin verir.

[Düzenle: asal sayıda kova kullanmak için daha özel bir neden daha var, bu da doğrusal problama ile çarpışmaları ele alıyorsanız. Daha sonra hashcode'dan bir adım hesaplarsınız ve bu adım, kova sayımının bir faktörü olarak ortaya çıkarsa, başlangıçtan geri dönmeden önce yalnızca (bucket_count / stride) probları yapabilirsiniz. En çok kaçınmak istediğiniz durum elbette özel kasa olması gereken adım = 0'dır, ancak küçük bir tam sayıya eşit özel kasa kovası / sayımı / adımdan kaçınmak için, sadece bucket_count'u hazırlayabilir ve adım 0 olmadığı sürece sağlanır.]


Yan not olarak: hashCodes için k faktörünün mantıklı bir seçimi için bir tartışma burada: stackoverflow.com/q/1835976/21499
Hans-Peter Störr 16:30 '

9
bu harika bir cevap. "Bu şekilde biten dizeler arasında çarpıcı ilişkiler modulo 31 ve sonuna yakın aynı dizeler arasında çarpıcı ilişkiler modulo 2 ^ 32 elde edersiniz. Bu, hashtable davranışı ciddi şekilde berbat etmez. " Özellikle 2 ^ 32 bölümünü anlamıyorum
sıradan

2
Bu konuda daha net şeyler yapmak için ek not: "Tüm karmaları ortak faktör eşit modulo çıkıyor" -> Bunun nedeni, eğer hash fonksiyonunun hash = 1st char + 2nd char * k + ... aynı ilk karaktere sahip dizeler alır,% k hash değeri bu dizeler için aynı olur. M, hashtable'ın boyutu ve g, M ve k'nin gcd'si ise, (hash% k)% g hash% g'ye eşittir (g, k'yi böldüğü için) ve dolayısıyla hash% g da bu dizeler için aynı olacaktır. Şimdi (hash% M)% g'yi düşünün, bu hash% g'ye eşittir (g M'yi böldüğünden). Yani (hash% M)% g tüm bu dizeler için eşittir.
Quark

1
@DanielMcLaury Joshua Bloch , Java için neden açıkladı - iki popüler kitapta (K&R, Dragon kitabı) önerildi ve İngilizce sözlükte düşük çarpışmalarla iyi performans gösterdi. Hızlıdır ( Horner yöntemini kullanır ). Görünüşe göre K&R bile nereden geldiğini hatırlamıyor. Benzer fonksiyon Rabin-Karp algoritmasından (1981) Rabin parmak izidir , ancak K&R (1978) bundan önce gelir.
bain

1
@SteveJessop, lütfen "Sonuna yakın hariç aynı dizeler arasında çarpıcı ilişkiler modulo 2 ^ 32" açıklayabilir misiniz? Teşekkürler.
Khanna111

29

Hash tablosunu eklerken / yeniden alırken yaptığınız ilk şey, verilen anahtar için hashCode değerini hesaplamak ve sonra hashCode% table_length yaparak hashCode öğesini hashTable boyutuna kırparak doğru kova bulmaktır. İşte muhtemelen bir yerde okuduğunuz 2 'ifade'

  1. Table_length için 2 gücü kullanırsanız, (hashCode (anahtar)% 2 ^ n) bulmak (hashCode (anahtar) & (2 ^ n -1)) kadar basit ve hızlıdır. Ancak, belirli bir anahtar için hashCode'u hesaplama işleviniz iyi değilse, kesinlikle birkaç karma kovada birçok anahtarın kümelenmesinden muzdarip olacaksınız.
  2. Ancak table_length için asal sayılar kullanırsanız, hesaplanan hashCodes, biraz aptal bir hashCode işleviniz olsa bile farklı karma kovalara eşlenebilir.

Ve işte kanıt.

Eğer hashCode işlevinizin diğer {x, 2x, 3x, 4x, 5x, 6x ...} arasında aşağıdaki hashCodes ile sonuçlandığını varsayalım, tüm bunlar m = table_length / GreatestCommonFactor şeklinde gruplandırılır (tablo_uzunluğu, x). (Bunu doğrulamak / türetmek önemsizdir). Artık kümelemeyi önlemek için aşağıdakilerden birini yapabilirsiniz

{X, 2x, 3x, 4x, 5x, 6x ...} gibi başka bir hashCode'un katları olan çok fazla hashKodu oluşturmadığınızdan emin olun.Ancak hashTable'ınızın olması gerekiyorsa bu biraz zor olabilir. milyonlarca giriş. Veya GreatestCommonFactor (table_length, x) değerini 1'e eşitleyerek m'yi table_length değerine eşit hale getirin, yani x ile table_length coprime yaparak. Ve x hemen hemen herhangi bir sayı olabilirse, table_length öğesinin asal sayı olduğundan emin olun.

Gönderen - http://srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime-numbers.html


11

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/

Resimlerle de oldukça açık bir açıklama.

Düzenleme: Özet olarak, değerler seçilen asal sayı ile çarpılır ve hepsini toplanırken benzersiz bir değer elde etme şansınız en yüksek olduğu için primerler kullanılır. Örneğin, bir dize verildiğinde, her harf değerini asal sayı ile çarpıp ardından hepsini eklemeniz karma değerini verecektir.

Daha iyi bir soru, neden tam olarak 31 sayısı?


5
Bir özetin yararlı olacağını düşünüyorum, ancak sitenin ölmesi durumunda, içeriğinin bir kısmı burada SO'ya kaydedilecektir.
Thomas Owens

2
Makale nedenini açıklamıyor, ancak "Araştırmacılar 31 asal kullanmanın anahtarlara daha iyi bir dağıtım sağladığını ve daha az çarpışma olmadığını buldu. .
theschmitzer

> Daha iyi bir soru, neden tam olarak 31 sayısı? 31 sayısının neden kullanıldığını kastediyorsanız, işaret ettiğiniz makale size nedenini söyler, çünkü çoklu olarak hızlı olduğu için ve cos testleri kullanmak için en iyisi olduğunu gösterir. Gördüğüm diğer popüler çarpan, hız sorununun (en azından başlangıçta) önemli bir faktör olduğu teorisine ağırlık veren 33'tür. Eğer demek istiyorsan, testlerde daha iyi yapan 31 hakkında ne var, o zaman korkarım bilmiyorum.
sgmoore

Tam olarak, çarpan olarak kullanılabilmesinin tek nedeni, çarpmanın kolay olmasıydı. (33'ün çarpan olarak kullanıldığını gördüğümde, son zamanlarda demek istemiyorum, muhtemelen on yıllar önceydi ve hash konusunda çok fazla analiz yapılmadan önce mümkün).
sgmoore

3
@SteveJessop 31 sayısı, CPU tarafından *32basit bir bit kaydırma, hatta daha iyi bir adres ölçek faktörü (örn lea eax,eax*8; leax, eax,eax*4. X86 / x64'te) olan bir (x * 32) -1 işlemi olarak kolayca optimize edilebilir . Yani *31asal sayı çoğalması için iyi bir adaydır. Bu birkaç yıl önce hemen hemen doğruydu - şimdi en son CPU mimarisinin neredeyse anında bir çarpımı var - bölünme her zaman daha yavaş ...
Arnaud Bouchez

10

tl; Dr.

index[hash(input)%2]olası tüm karmaların yarısı ve bir dizi değer için bir çarpışma ile sonuçlanacaktır. index[hash(input)%prime]olası tüm karma değerlerin <2 değerinde bir çarpışma ile sonuçlanır. Böleni tablo boyutuna sabitlemek, sayının tablodan daha büyük olmamasını da sağlar.


1
2 asal sayı dostum
Ganesh Chowdhary Sadanala

8

Astarlar kullanılır, çünkü polinom modulo P kullanan tipik bir karma işlevi için benzersiz bir değer elde etme şansınız yüksektir. Bu, 2 farklı polinomun aynı modulo P değerini ürettiği anlamına gelir. Bu polinomların farkı yine aynı N derecesinde (veya daha az) bir polinomdur. N'den fazla kökü yoktur (bu, matematiğin doğasını gösterir, çünkü bu iddia sadece bir alandaki polinom için geçerlidir => asal sayı). Yani N, P'den çok daha azsa, muhtemelen bir çarpışma yaşamayacaksınız. Bundan sonra, deney muhtemelen 37'nin 5-10 uzunluğundaki karma tablolar için çarpışmalardan kaçınacak kadar büyük olduğunu ve hesaplamalar için kullanılacak kadar küçük olduğunu gösterebilir.


1
Açıklama şimdi açık gibi görünse de, A.Shen "Programlama: Teoremler ve problemler" (Rusça) tarafından bir kitap okuduktan sonra bana geldi, Rabin algoritması tartışmasına bakın. İngilizce çevirinin olup olmadığından emin değilim.
TT_

5

Sadece alternatif bir bakış açısı sağlamak için bu site var:

http://www.codexon.com/posts/hash-functions-the-modulo-prime-myth

Bu, asal sayıdaki kovaya yuvarlamak yerine mümkün olan en fazla sayıda kova kullanmanız gerektiğini iddia eder. Makul bir olasılık gibi görünüyor. Sezgisel olarak, daha fazla sayıda kepçenin nasıl daha iyi olacağını kesinlikle görebiliyorum, ancak bunun matematiksel bir argümanını yapamıyorum.


Daha fazla kova sayısı daha az çarpışma anlamına gelir: Güvercin deliği prensibine bakın.
Bilinmiyor

11
@Bilinmeyen: Bunun doğru olduğuna inanmıyorum. Lütfen yanılıyorsam beni düzeltin, ancak güvercin deliği prensibinin hash tablolarına uygulanmasının, çarpışmaların miktarı veya yoğunluğu hakkında herhangi bir sonuç çıkarmamak için kutulardan daha fazla elemanınız varsa çarpışma olacağını iddia etmenize izin verdiğine inanıyorum. Yine de, daha fazla sayıda kutunun doğru yol olduğuna inanıyorum.
Falaina

Çarpışmaların tüm niyetler ve amaçlar için rastgele olduğunu varsayarsanız, doğum günü paradoksu ile daha büyük bir alan (kovalar) bir çarpışma olasılığını azaltacaktır.
Bilinmiyor

1
@Bilinmeyen çarpışmaların karma işlevinin kendisine bağlı olduğunu da bilmiyorsunuz. Eğer işlevi gerçekten kötü ise, o zaman boyutu ne kadar büyük olursa olsun, hala önemli miktarda çarpışma olabilir
Suraj Chandran

Orijinal makale gitmiş gibi görünüyor, ancak orijinal yazarla bir tartışma da dahil olmak üzere burada bazı içgörülü yorumlar var. news.ycombinator.com/item?id=650487
Adrian McCarthy

3

Asal değerler benzersiz numaralardır. Onlar benzersizdir, başka bir sayıya sahip bir asal ürünün, onu oluşturmak için bir asal kullanılması nedeniyle benzersiz olma şansına sahiptir (tabii ki asalın kendisi kadar benzersiz değildir). Bu özellik karma işlevlerinde kullanılır.

“Samuel” dizesi verildiğinde, kurucu basamakların veya harflerin her birini bir asal sayı ile çarpıp ekleyerek benzersiz bir karma oluşturabilirsiniz. Bu nedenle primerler kullanılır.

Ancak primer kullanmak eski bir tekniktir. Buradaki anahtar, yeterince benzersiz bir anahtar üretebildiğiniz sürece diğer karma tekniklere de geçebileceğinizi anlamaktır. Bu konu hakkında daha fazla bilgi için http://www.azillionmonkeys.com/qed/hash.html adresine gidin.

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/


1
hahahah .... aslında 2 asal ürünün asal ve başka herhangi bir sayının çarpımından daha 'benzersiz' olma şansı yok mu?
HasaniH

@Beska Burada "teklik" özyinelemeli olarak tanımlanmıştır, bu yüzden "teksizlik" in aynı şekilde tanımlanması gerektiğine inanıyorum :)
TT_

3

Karma işlevinin seçimine bağlıdır.

Birçok hash fonksiyonu, verilerdeki çeşitli elemanları, makinenin kelime boyutuna karşılık gelen ikisinin gücünü modülo ile birleştirerek birleştirir (bu modül sadece hesaplama taşmasına izin vererek ücretsizdir).

Bir veri öğesinin çarpanı ile karma tablonun boyutu arasında ortak bir faktör istemezsiniz, çünkü o zaman veri elemanını değiştirmek verileri tüm tabloya yaymaz. Tablonun boyutu için bir ana seçerseniz, böyle bir ortak faktör oldukça düşüktür.

Öte yandan, bu faktörler genellikle garip primerlerden oluşur, bu nedenle karma tablonuz için iki güç kullanarak güvenli olmalısınız (örneğin, Eclipse, Java hashCode () yöntemini oluştururken 31 kullanır).


2

Tablo boyutunuzun (veya modulo sayısının) T = (B * C) olduğunu varsayalım. Şimdi girdiniz için karma (N * A * B) gibidir, burada N herhangi bir tam sayı olabilir, o zaman çıktınız iyi dağıtılmaz. Her n, C, 2C, 3C vs. olduğunda, çıktınız tekrarlanmaya başlayacaktır. yani çıktınız yalnızca C konumlarına dağıtılacaktır. Burada C'nin (T / HCF (tablo boyutu, karma)) olduğuna dikkat edin.

Bu sorun HCF 1 kullanılarak giderilebilir. Asal sayılar bunun için çok iyidir.

Bir başka ilginç şey T 2 ^ N olduğunda. Bunlar çıktıyı, giriş karmasının tüm düşük N bitleriyle tam olarak aynı verecektir. Her sayı 2'nin gücünü temsil edebileceğinden, T ile herhangi bir sayının modulo'unu alacağımızda, 2 form numarasının tüm güçlerini> = N olan çıkarırız, bu nedenle girişe bağlı olarak her zaman belirli desen sayısını veririz . Bu da kötü bir seçim.

Benzer şekilde, 10 ^ N olarak T de benzer nedenlerden dolayı kötüdür (ikili yerine sayıların ondalık gösterimlerinde desen).

Bu nedenle, asal sayılar daha iyi dağıtılmış sonuçlar verme eğilimindedir, bu nedenle tablo boyutu için iyi bir seçimdir.


2

Diğer yanıtımdan kopyalama https://stackoverflow.com/a/43126969/917428 . Daha fazla ayrıntı ve örnek için bakınız.

Ben sadece bilgisayarların temel 2 ile çalıştığı gerçeği ile ilgili olduğuna inanıyorum. Sadece aynı şeyin temel 10 için nasıl çalıştığını düşünün:

  • % 8 10 = 8
  • % 18 10 = 8
  • 87865378% 10 = 8

Sayının ne olduğu önemli değil: 8 ile bittiği sürece, modulo 10 8 olacaktır.

Yeterince büyük, güçten iki olmayan bir sayı seçmek, hash işlevinin, bir alt kümesinden ziyade, tüm giriş bitlerinin bir işlevi olduğundan emin olur.


1

Steve Jessop'un cevabı için bir şeyler eklemek istiyorum (Yeteri kadar itibarım olmadığından yorum yapamıyorum). Ama bazı yararlı materyaller buldum. Cevabı çok yardımcı oldu ama bir hata yaptı: kova boyutu 2'nin gücü olmamalı. Thomas Cormen, Charles Leisersen, ve ark.

Bölme yöntemini kullanırken, genellikle m'nin belirli değerlerinden kaçınırız. Örneğin, m 2 gücü olmamalıdır, çünkü m = 2 ^ p ise, h (k) sadece k'nin en düşük dereceli bitleri olur. Tüm düşük dereceli p-bit modellerinin eşit derecede olası olduğunu bilmedikçe, hash işlevini anahtarın tüm bitlerine bağlı olacak şekilde tasarlamamız daha iyidir. Alıştırma 11.3-3'te göstermenizi istediği gibi, k = 2 ^ p ile yorumlanan bir karakter dizgisiyken m = 2 ^ p-1'i seçmek kötü bir seçim olabilir, çünkü k karakterlerine izin vermek karma değerini değiştirmez.

Umarım yardımcı olur.


0

Bir karma fonksiyonu için sadece genel olarak çarpışmaları en aza indirmek değil, aynı zamanda birkaç bayt değiştirirken aynı karma ile kalmayı imkansız kılmak da önemlidir.

Diyelim ki ve (x + y*z) % key = xile bir denkleminiz var . Anahtar bir birincil sayı ise n * y = anahtar N'deki her n için doğrudur ve diğer her sayı için false olur.0<x<key0<z<key

Anahtarın asal bir örnek olmadığı bir örnek: x = 1, z = 2 ve anahtar = 8 / z = 4 anahtarı hala doğal bir sayı olduğundan, 4 denklemimiz için bir çözüm haline gelir ve bu durumda (n / 2) * y = anahtar N'deki her n için doğrudur. Denklem için çözelti miktarı pratik olarak iki katına çıkmıştır, çünkü 8 asal değildir.

Saldırganımız 8'in denklem için olası bir çözüm olduğunu zaten biliyorsa, dosyayı 8'den 4'e değiştirebilir ve yine de aynı karmayı alır.


0

Yukarıdaki popüler cevapların bazılarında bağlantılı popüler wordpress web sitesini okudum. Anladığım kadarıyla, yaptığım basit bir gözlemi paylaşmak istiyorum.

Makaledeki tüm ayrıntıları burada bulabilirsiniz , ancak aşağıdakilerin geçerli olduğunu varsayalım:

  • Asal sayı kullanmak bize benzersiz bir değerin "en iyi şansını" verir

Genel bir hashmap uygulaması 2 şeyin benzersiz olmasını ister.

  • Anahtar için benzersiz karma kodu
  • Gerçek değeri saklamak için benzersiz dizin

Benzersiz endeksi nasıl elde ederiz? İç konteynerin başlangıç ​​boyutunu da birincil haline getirerek. Temel olarak, asal söz konusu olduğu için, ID nesnelerine kullandığımız ve dahili kapsayıcı içinde dizinler bulduğumuz benzersiz sayılar üretmenin bu eşsiz özelliğine sahiptir.

Misal:

anahtar = "anahtar"

değer = "değer" uniqueId = "k" * 31 ^ 2 + "e" * 31 ^ 1` + "y"

benzersiz kimliğe eşler

Şimdi değerimiz için benzersiz bir konum istiyoruz - bu yüzden

uniqueId % internalContainerSize == uniqueLocationForValue, varsayım internalContainerSizeda asaldır.

Bunun basitleştirildiğini biliyorum, ama genel fikri ele almayı umuyorum.


0

Asal güç modülleri ile ilgili "matematiğin doğası", sınırlı bir alanın yapıtaşı olduklarıdır . Diğer iki yapı taşı bir toplama ve çarpma işlemidir. Ana modüllerin özel özelliği, sadece modüle alınan "düzenli" toplama ve çarpma işlemleriyle sonlu bir alan oluşturmalarıdır. Bu, her çarpımın farklı bir tamsayı modülo ile eşleştiği anlamına gelir, her eklenti de öyle.

Ana modüller avantajlıdır çünkü:

  • İkincil karma işleminde ikincil çarpanı seçerken en fazla özgürlüğü verirler, 0 dışındaki tüm çarpanlar tüm elemanları tam olarak bir kez ziyaret eder.
  • Eğer tüm karmalar modül değerinden daha azsa, hiç çarpışma olmaz
  • Rastgele primerler iki modülün gücünden daha iyi karışır ve sadece bir alt küme değil tüm bitlerin bilgilerini sıkıştırır

Ancak büyük bir dezavantajı var, modern bir CPU'da bile birçok (~ 15-40) döngü alan bir tamsayı bölünmesine ihtiyaç duyuyorlar. Hesaplamanın yaklaşık yarısı ile, karmaın çok iyi karıştırıldığından emin olabilirsiniz. İki çarpma ve xorshift işlemi, birincil bir modülden daha iyi karışacaktır. Daha sonra herhangi bir karma tablo boyutu ve karma azaltma en hızlı olanı kullanabiliriz, 2 tablo boyutunun gücü için toplam 7 işlem ve keyfi boyutlar için yaklaşık 9 işlem veririz.

Son zamanlarda en hızlı hash tablosu uygulamalarının çoğuna baktım ve çoğu asal modülleri kullanmıyor.


0

Bu soru daha uygun soru ile birleştirildi, hash tabloları neden 2 büyüklüğünde değil asal boyutlu diziler kullanmalı, hash işlevleri için burada birçok iyi yanıt var, ancak ilgili soru için bazı güvenlik açısından kritik hash tabloları neden , glibc gibi, asal boyutlu diziler kullanın, henüz yok.

Genellikle 2 tablonun gücü çok daha hızlıdır. Orada h % n => h & bitmaskbitmask clzn boyutu ("önde gelen sıfırları say") üzerinden hesaplanabilir pahalı . Bir modulo fonksiyonunun bir mantıksaldan yaklaşık 50 kat daha yavaş olan tamsayı bölünmesi yapması gerekir and. Bir modulodan kaçınmak için bazı hileler vardır, örneğin Lemire'nin https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ gibi , ancak genellikle hızlı karma tablolar güç kullanır 2 ve güvenli hash tabloları asal kullanır.

Neden öyle?

Bu durumda güvenlik, çoğu karma tablo ile bağlantılı bir çarpışma listesinde yalnızca doğrusal arama olan çarpışma çözümleme stratejisine yapılan saldırılarla tanımlanır. Veya daha hızlı açık adresleme tabloları ile doğrudan tablodan doğrusal arama yapın. Yani 2 tablonun gücü ve tablonun bazı dahili bilgileri, örneğin bazı JSON arabirimi tarafından sağlanan anahtarlar listesinin boyutu veya sırası ile, kullanılan doğru bit sayısını elde edersiniz. Bitmask üzerindekilerin sayısı. Bu tipik olarak 10 bitten daha düşüktür. Ve 5-10 bit için, en güçlü ve en yavaş hash fonksiyonları ile bile kaba kuvvet çarpışmaları önemsizdir. Artık 32bit veya 64 bit karma işlevlerinizin tam güvenliğini elde edemezsiniz. Ve mesele, üfürüm ve hatta sifa gibi canavarları değil, hızlı küçük hash işlevlerini kullanmaktır.

Dolayısıyla, karma tablonuza, bir DNS çözümleyici, bir programlama dili gibi harici bir arayüz sağlarsanız ... DOS gibi hizmetleri kötüye kullananlara dikkat etmek istersiniz. Bu tür kişilerin kamu hizmetinizi çok daha kolay yöntemlerle kapatması normalde daha kolaydır, ancak oldu. Bu yüzden insanlar umursuyordu.

Bu nedenle bu tür çarpışma saldırılarından korunmak için en iyi seçenekler

1) asal tabloları kullanmak, çünkü o zaman

  • 32 veya 64 bitin tamamı, yalnızca birkaç tanesini değil, aynı zamanda kovayı bulmak için de geçerlidir.
  • karma tablo yeniden boyutlandırma fonksiyonu sadece iki kat daha doğaldır. En iyi büyüme fonksiyonu fibonacci dizisidir ve primerler ikiye katlanmaktan daha yakındır.

2) 2 boyutlu hızlı güçle birlikte gerçek saldırıya karşı daha iyi önlemler kullanın.

  • çarpışmaları sayın ve tespit edilen saldırılarda iptal edin veya uyuyun, bu da <% 1 olasılıkla çarpışma sayısıdır. 100 bit 32 bit karma tablolar gibi. Örneğin djb'nin dns çözümleyicisi bunu yapar.
  • bir çarpışma saldırısı algılandığında O (n) değil, O (log n) araması ile bağlantılı çarpışma listesini ağaçlara dönüştürün. Java böyle yapar.

Daha güvenli hash işlevlerinin, bu tür saldırıları önlemeye yardımcı olduğu, açıkladığım gibi yanlış olan geniş bir efsane var. Yalnızca düşük bitlerle güvenlik yoktur. Bu yalnızca asal boyutlu tablolarla çalışır, ancak bu, en yavaş iki yöntem olan yavaş karma artı yavaş asal modulo kombinasyonunu kullanır.

Karma tablolar için karma işlevlerinin öncelikle küçük (inlinable olması) ve hızlı olması gerekir. Güvenlik sadece çarpışmalarda doğrusal aramayı önlemekten gelebilir. Bazı değerlere duyarsız olanlar gibi önemsiz derecede hash işlevlerini kullanmamak (çarpma kullanılırken \ 0 gibi).

Rastgele tohumları kullanmak da iyi bir seçenektir, insanlar önce bu ile başlar, ancak masanın yeterli bilgisi ile rastgele bir tohum bile çok yardımcı olmaz ve dinamik diller genellikle depolandığı için tohumu diğer yöntemlerle almayı önemsiz hale getirir. bilinen bellek konumları.


-1
function eratosthenes(n) {

    function getPrime(x) {
        var middle = (x-(x%2))/2;
        var arr_rest = [];
        for(var j=2 ; j<=middle;j++){
            arr_rest.push(x%j);
        }

        if(arr_rest.indexOf(0) == -1) {
            return true
        }else {
            return false
        }

    }
    if(n<2)  {
        return []
    }else if(n==2){
        return [2]
    }else {
        var arr = [2]
        for(var i=3;i<n;i++) {
            if(getPrime(i)){
                arr.push(i)
            }
        }
    }

    return arr;
}

2
Çözümünüzü açıklamak için yorum ekleyebilir misiniz?
pom421
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.