Karma tablolar gerçekten O (1) olabilir mi?


114

Karma tabloların O (1) 'e ulaşabileceği yaygın bir bilgi gibi görünüyor, ancak bu bana hiçbir zaman anlamlı gelmedi. Lütfen birisi açıklayabilir mi? İşte akla gelen iki durum:

A. Değer, hash tablosunun boyutundan daha küçük bir int. Bu nedenle, değer kendi hash'idir, dolayısıyla hash tablosu yoktur. Ama olsaydı, O (1) olurdu ve yine de verimsiz olurdu.

B. Değerin bir karmasını hesaplamalısınız. Bu durumda, aranan verinin boyutu için sıra O (n) olur. O (n) işini yaptıktan sonra arama O (1) olabilir, ama bu yine de gözlerimde O (n) 'ye çıkıyor.

Ve mükemmel bir hash veya büyük bir hash tablonuz yoksa, muhtemelen kova başına birkaç öğe vardır. Böylece, bir noktada zaten küçük bir doğrusal aramaya dönüşür.

Hash tablolarının harika olduğunu düşünüyorum, ancak sadece teorik olması gerekmedikçe O (1) tanımını alamıyorum.

Wikipedia'nın karma tablolar makalesi, sürekli olarak sabit arama süresine atıfta bulunur ve karma işlevinin maliyetini tamamen göz ardı eder. Bu gerçekten adil bir ölçü mü?


Düzenleme: Öğrendiklerimi özetlemek gerekirse:

  • Teknik olarak doğrudur çünkü anahtardaki tüm bilgileri kullanmak için hash fonksiyonuna gerek yoktur ve bu nedenle sabit zaman olabilir ve yeterince büyük bir tablo çarpışmaları neredeyse sabit zamana indirebilir.

  • Pratikte doğrudur çünkü zamanla, hash işlevi ve tablo boyutu çarpışmaları en aza indirecek şekilde seçildiği sürece çalışır, ancak bu genellikle sabit bir zaman karma işlevi kullanmamak anlamına gelir.


31
O (1) değil, O (1) itfa edildi.
kennytm

O () 'nun çok sayıda işlem için sınır olduğunu unutmayın. 'Ortalama' olarak pek çok çarpışma yaşamazsınız - tek bir operasyonun çarpışmaması gerekmez.
Martin Beckett

Dize uygulamasına bağlı olarak, dizeler kendi hashed değerlerini yanlarında taşıyabilir, bu nedenle bu sabit olur. Önemli olan, hash arama karmaşıklığı ile alakasız olmasıdır.
Rich Remer

@kennytm Elbette, girişe hash uyguladığınızda arama amortize edilir O (1). Ancak hashi hesaplamanın maliyeti gerçekten ihmal edilebilir mi? Bir dizgeye hashing yaptığımızı varsayalım - bir karakter dizisi. Karma oluşturmak için, her karakter yinelenir, bu nedenle bir dizeye hashing O (N) olur, burada N, dizenin uzunluğudur. C # için bu şekilde belgelenmiştir ve bu, hashCode()bir String. grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…
spaaarky21

1
@ spaaarky21 Bahsettiğiniz O (N) 'deki N, karma tablonun boyutundaki n'den farklı olan string uzunluğudur. Mark Byer'in cevabı zaten bu konuyu ele aldı.
kennytm

Yanıtlar:


65

Burada iki değişkeniniz var, m ve n; burada m, girişin uzunluğu ve n, karmadaki öğe sayısıdır.

O (1) arama performansı iddiası en az iki varsayımda bulunur:

  • Nesneleriniz O (1) zamanında eşit olarak karşılaştırılabilir.
  • Birkaç hash çarpışması olacak.

Nesneleriniz değişken boyuttaysa ve eşitlik kontrolü tüm bitlere bakmayı gerektiriyorsa, performans O (m) olacaktır. Ancak hash fonksiyonunun O (m) olması gerekmez - O (1) olabilir. Bir kriptografik hash'den farklı olarak, bir sözlükte kullanılacak bir hash fonksiyonunun, hash'i hesaplamak için girdideki her bit'e bakması gerekmez. Uygulamalar sadece sabit sayıda bite bakmakta özgürdür.

Yeterince çok sayıda öğe için, öğelerin sayısı olası hash sayısından daha fazla olacak ve daha sonra performansın O (1) üzerine çıkmasına neden olan çarpışmalar alacaksınız, örneğin basit bir bağlantılı liste geçişi (veya O (n) için O (n) * m) her iki varsayım da yanlışsa).

Uygulamada, O (1) iddiası teknik olarak yanlış olsa da, birçok gerçek dünya durumu için ve özellikle yukarıdaki varsayımların geçerli olduğu durumlar için yaklaşık olarak doğrudur.


4
Yukarıdakilerin yanı sıra, anahtarlarınız olarak değişmez nesneler kullanıyorsanız, örneğin Java Dizeleri, karmayı bir kez hesapladıktan sonra, bunu hatırlayabilir ve tekrar hesaplamanıza gerek kalmaz. Öte yandan, doğru bölümü bulduktan sonra iki anahtarın eşit olup olmadığını söylemek için genellikle hash'e güvenemezsiniz, bu nedenle dizeler için eşit olup olmadıklarını bulmak için bir O (m) geçişi yapmanız gerekir.
JeremyP

1
@JeremyP: O (m) eşitliği karşılaştırmasında iyi bir nokta. Bunu kaçırdım - güncellenmiş gönderiyi. Teşekkürler!
Mark Byers

2
Bu O(1)iddia doğrudur, e-postaları intveya bir makine kelimesine uyan başka bir şeyi karıştırıyorsanız . Hashing üzerine çoğu teorinin varsaydığı şey budur.
Thomas Ahle

Seninki Mark hakkındaki bu açıklamanı beğendim, meshfields.de/hash-tables'daki
Steve K

3
In "m girişinin uzunluğudur" - giriş o eklenmekte bütün anahtarları ve değerleri anlamına gelebilir, ancak bu demek (en azından zaten konuyu anlamaya olanlara) sonradan ortaya çıkıyor - aşırı muğlak anahtarı . Açıklık için cevapta "anahtar" kullanılmasını öneriyorum. BTW - somut örnek - Görsel C ++ std::hashmetinsel anahtarlar, metin boyunca eşit aralıklarla 10 karakteri karma değerde birleştirir, böylece metin uzunluğuna bakılmaksızın O (1) olur (ancak GCC'den çok daha fazla çarpışmaya yatkındır!). Ayrı olarak, O (1) 'in iddiaları, m'nin n'den çok daha küçük olduğuna dair (normalde doğru olarak) başka bir varsayıma sahiptir .
Tony Delroy

22

Karmayı hesaplamanız gerekir, bu nedenle, aranan verinin boyutu için sıra O (n) olur. O (n) işini yaptıktan sonra arama O (1) olabilir, ama bu yine de gözlerimde O (n) 'ye çıkıyor.

Ne? Tek bir öğeye hash işlemi uygulamak sabit zaman alır. Neden başka bir şey olsun ki? nÖğeleri ekliyorsanız , evet, nkarmaları hesaplamanız gerekir ve bu doğrusal zaman alır ... bir öğeyi yukarı aramak için, aradığınız şeyin tek bir karmasını hesaplar, ardından bununla uygun kovayı bulun . Zaten hash tablosunda bulunan her şeyin karmasını yeniden hesaplamazsınız.

Ve mükemmel bir hash veya büyük bir hash tablonuz yoksa kova başına muhtemelen birkaç öğe vardır, bu yüzden yine de bir noktada küçük bir doğrusal aramaya dönüşür.

Şart değil. Paketlerin liste veya diziler olması gerekmez, dengeli bir BST gibi herhangi bir kap türü olabilirler. Bu O(log n)en kötü durum anlamına gelir . Ancak bu nedenle, tek bir kovaya çok fazla öğe koymamak için iyi bir hashing işlevi seçmek önemlidir. KennyTM'nin de belirttiği gibi O(1), ara sıra bir kovayı kazmak zorunda kalsanız bile , ortalama olarak yine de zamanınız olacak .

Hash tablolarının değiş tokuşu elbette alan karmaşıklığıdır. Zaman için uzayı takas ediyorsunuz, bu da bilgisayar biliminde olağan bir durum gibi görünüyor.


Diğer yorumlarınızdan birinde anahtar olarak dizeleri kullanmaktan bahsediyorsunuz. Birkaç karakterden oluştuğu için bir dizenin karmasını hesaplamak için gereken süre hakkında endişelisiniz? Başka birinin tekrar belirttiği gibi, hash'i hesaplamak için tüm karakterlere bakmanıza gerek yoktur, ancak yaparsanız daha iyi bir hash üretebilir. Bu durumda, manahtarınızda ortalama karakterler varsa ve bunların hepsini karma değerinizi hesaplamak için kullandıysanız, o zaman sanırım haklısınız, bu aramalar yeterli olacaktır O(m). Eğer m >> nöyleyse bir sorununuz olabilir. Bu durumda muhtemelen bir BST ile daha iyi durumda olursunuz. Veya daha ucuz bir hashing işlevi seçin.


karma tablolar BST kullanmaz. BST'ler karma değer gerektirmez. Haritalar ve Kümeler yine de BST'ler olarak uygulanabilir.
Nick Dandoulakis

3
@Nick: Eh? Hayır ... BST'ler karma değer gerektirmez ... önemli olan budur. Bu noktada zaten bir çarpışmaya sahip olduğumuzu varsayıyoruz (aynı hash ... veya en azından aynı kova), bu yüzden doğru elemanı, yani gerçek değeri bulmak için başka bir şeye bakmamız gerekiyor.
mpen

Oh, ne demek istediğini anlıyorum. Ancak BST'leri ve karmaları karıştırmanın zahmete değer olduğundan emin değilim. Neden sadece BST kullanmıyorsunuz?
Nick Dandoulakis

2
Sadece bunu söylüyorum olabilir o kurtulmak için O(n)çarpışmalar için. Eğer varsa edilmektedir çarpışmaların bir sürü bekliyoruz, o zaman muhtemelen daha iyi ilk etapta bir BST ile gidiş, haklısın.
mpen

1
@ spaaarky21 Doğru, ancak Nbu durumda dizenin uzunluğu. Hangi 'kova'nın girmesi gerektiğini belirlemek için yalnızca bir dizgeye hash uygulamamız gerekir - bu, karma haritanın uzunluğu ile büyümez.
mpen

5

Karma sabit boyuttadır - uygun karma grubunu aramak sabit maliyetli bir işlemdir. Bu, O (1) olduğu anlamına gelir.

Hash'i hesaplamanın özellikle pahalı bir işlem olması gerekmez - burada kriptografik hash fonksiyonlarından bahsetmiyoruz. Ama bu tarafından. Karma işlevi hesaplamasının kendisi, elemanların n sayısına bağlı değildir ; bir öğedeki verilerin boyutuna bağlı olsa da, n'nin ifade ettiği şey bu değildir . Dolayısıyla hash'in hesaplanması n'ye bağlı değildir ve aynı zamanda O (1) 'dir.


3
Hash kovasına bakıldığında O (1). Ancak doğru anahtarı bulmak, bir O (n) prosedürüdür, burada n, hash çarpışmalarının sayısına bağlıdır.
Nick Dandoulakis

1
Yani 3 adımda, hash hesaplayın, kovayı bulun, kovayı arayın, orta adım sabit mi? Kovayı aramak genellikle sabittir. Hash'i hesaplamak, genellikle diğer kovayı bulma yöntemlerinden birkaç kat daha ucuzdur. Ama bu gerçekten sabit zamana eşit mi? Saf bir alt dize aramasında, iki uzunluk için O (n * m) diyeceksiniz, öyleyse burada anahtarın uzunluğu neden göz ardı ediliyor?
çekiliş

Sabit uzunlukta bir anahtar bulmak yalnızca O (n) 'dur, ancak listesi desteklenmişse, dengeli ağaç destekli bir özet tablosu O (log (n))
jk olacaktır.

@Jk İyi hash fonksiyonları için, en kötü durum her zaman logn, cevabımı stackoverflow.com/questions/4553624/hashmap-get-put-complexity/…
Thomas Ahle

En kötü durumda, çarpışma durumunda karmaşıklık o (n) olacaktır
Saurabh Chandra Patel

3

Hashing, sadece tabloda sabit sayıda anahtar varsa ve diğer bazı varsayımlar yapıldığında O (1) 'dir. Ancak bu gibi durumlarda avantajı vardır.

Anahtarınızın n-bit gösterimi varsa, hash fonksiyonunuz bu bitlerin 1, 2, ... n'sini kullanabilir. 1 bit kullanan bir hash işlevi hakkında düşünmek. Kesinlikle O (1) 'dir. Ama sadece anahtar boşluğunu 2'ye bölüyorsunuz. Yani aynı bölmeye 2 ^ (n-1) anahtar eşlemelisiniz. BST aramasını kullanarak bu, neredeyse doluysa belirli bir anahtarı bulmak için n-1 adıma kadar sürer.

Karma işleviniz K bit kullanıyorsa, bölme boyutunuzun 2 ^ (nk) olduğunu görmek için bunu genişletebilirsiniz.

dolayısıyla, çarpışmaları çözmek için K-bit hash fonksiyonu ==> en fazla 2 ^ K etkin bölme ==> bin ==> (nK) adım (BST) başına en fazla 2 ^ (nK) n-bit anahtar. Aslında çoğu karma işlevi çok daha az "etkilidir" ve 2 ^ k kutu üretmek için K bitinden daha fazlasına ihtiyaç duyar / kullanır. Yani bu bile iyimser.

Bunu bu şekilde görüntüleyebilirsiniz - en kötü durumda n bitlik bir anahtar çiftini benzersiz şekilde ayırt edebilmek için ~ n adıma ihtiyacınız olacaktır. Bu bilgi teorisi sınırını, hash tablosunu aşmanın gerçekten bir yolu yok.

Ancak, karma tabloyu bu şekilde / ne zaman kullanacağınız DEĞİLDİR!

Karmaşıklık analizi, n-bit anahtarlar için, tabloda O (2 ^ n) anahtarlara sahip olabileceğinizi varsayar (örneğin, tüm olası anahtarların 1 / 4'ü). Ancak, karma tabloyu kullandığımız her zaman olmasa da çoğu, tabloda yalnızca sabit sayıda n-bit anahtarına sahibiz. Tabloda yalnızca sabit sayıda anahtar istiyorsanız, örneğin C maksimum sayınızdır, o zaman beklenen sabit çarpışmayı garanti eden (iyi bir hash fonksiyonu ile) O (C) bölmelerinden oluşan bir karma tablosu oluşturabilirsiniz; ve anahtardaki n bitin ~ logC'sini kullanan bir hash fonksiyonu. Daha sonra her sorgu O (logC) = O (1) olur. İnsanlar "karma tablo erişiminin O (1)" olduğunu iddia etme şeklidir /

Burada birkaç sorun var - ilk olarak, tüm bitlere ihtiyacınız olmadığını söylemek yalnızca bir faturalama numarası olabilir. Öncelikle anahtar değerini hash fonksiyonuna gerçekten iletemezsiniz, çünkü bu, bellekte O (n) olan n biti hareket ettirir. Yani örneğin bir referans geçişi yapmanız gerekir. Ama yine de onu bir O (n) işlemi olan bir yerde saklamanız gerekiyor; sadece hashing'e fatura etmezsiniz; genel hesaplama göreviniz bundan kaçınamaz. İkinci olarak, hash işlemini yaparsınız, bölmeyi bulursunuz ve 1'den fazla anahtar bulursunuz; maliyetiniz çözünürlük yönteminize bağlıdır - karşılaştırma tabanlı (BST veya Liste) yaparsanız, O (n) işleminiz olur (geri çağırma anahtarı n-bittir); 2. hash yaparsanız, 2. hash çakışma varsa aynı sorunu yaşarsınız.

Bu durumda alternatifi, örneğin BST'yi düşünün. C tuşları vardır, bu nedenle dengeli bir BST derinlemesine O (logC) olacaktır, bu nedenle arama O (logC) adımlarını alır. Bununla birlikte, bu durumda karşılaştırma bir O (n) işlemi olacaktır ... bu nedenle hashing bu durumda daha iyi bir seçimdir.


1

TL; DR: Hash tabloları O(1), hash fonksiyonunuzu evrensel bir hash fonksiyonları ailesinden rastgele bir şekilde seçerseniz beklenen en kötü durum süresini garanti eder . Beklenen en kötü durum, ortalama durumla aynı değildir.

Yasal Uyarı: Karma tabloların olduğunu resmen kanıtlamıyorum O(1), çünkü bu videoya Coursera'dan [ 1 ] bir göz atın . Ayrıca hash tablolarının amortize edilmiş yönlerini de tartışmıyorum . Bu, hash ve çarpışmalar hakkındaki tartışmaya ortogonaldir.

Diğer cevaplarda ve yorumlarda bu konuyla ilgili şaşırtıcı derecede büyük bir kafa karışıklığı görüyorum ve bu uzun cevapta bazılarını düzeltmeye çalışacağım.

En kötü durum hakkında akıl yürütme

Farklı en kötü durum analizi türleri vardır. Şimdiye kadar burada çoğu yanıtın yaptığı analiz en kötü durum değil , daha ziyade ortalama bir durumdur. [ 2 ]. Ortalama vaka analizi daha pratik olma eğilimindedir. Belki algoritmanızın bir en kötü durum girdisi vardır, ancak aslında diğer tüm olası girdiler için iyi çalışır. Sonuç olarak, çalışma zamanınız üzerinde çalıştığınız veri kümesine bağlıdır .

getBir karma tablo yönteminin aşağıdaki sözde kodunu düşünün . Burada, çarpışmayı zincirleme yoluyla ele aldığımızı varsayıyorum, bu nedenle tablonun her girişi bağlantılı bir listedir.(key,value) çift . Ayrıca, grup sayısının msabit olduğunu O(n), ancak ngirişteki öğelerin sayısı nerede olduğunu varsayıyoruz .

function get(a: Table with m buckets, k: Key being looked up)
  bucket <- compute hash(k) modulo m
  for each (key,value) in a[bucket]
    return value if k == key
  return not_found

Diğer yanıtların da işaret ettiği gibi, bu ortalama O(1)ve en kötü durumda çalışır O(n). Burada meydan okuyarak bir ispatın küçük bir taslağını yapabiliriz. Zorluk şu şekildedir:

(1) Karma tablo algoritmanızı bir düşmana verirsiniz.

(2) Düşman onu inceleyebilir ve istediği kadar hazırlık yapabilir.

(3) Nihayet düşman size bir boyut girdisi verir n , tablonuza eklemeniz için size bir .

Soru şu: hash tablonuz rakip girdilere göre ne kadar hızlı?

Adım (1) 'den düşman, hash fonksiyonunuzu bilir; adım (2) sırasında düşman, örneğin bir grup öğenin karmasını rasgele hesaplayarak, naynı öğelerin bir listesini oluşturabilir hash modulo m; ve sonra (3) 'te size bu listeyi verebilirler. Ama bakalım, tüm nöğeler aynı gruba hash oluşturduğundan, algoritmanızın bu O(n)paketteki bağlantılı listeyi geçmesi zaman alacaktır . Meydan okumayı kaç kez denersek deneyelim, rakip her zaman kazanır ve algoritmanız bu kadar kötüdür, en kötü durumO(n) .

Hashing nasıl O (1) olur?

Önceki zorlukta bizi şaşırtan şey, düşmanın hash işlevimizi çok iyi bilmesi ve bu bilgiyi mümkün olan en kötü girdiyi oluşturmak için kullanabilmesiydi. Ya her zaman tek bir sabit karma işlevi kullanmak yerine, aslında Halgoritmanın çalışma zamanında rastgele seçebileceği bir dizi karma işlevimiz olsaydı ? Merak ediyorsanız H, buna evrensel bir hash fonksiyonları ailesi denir [ 3 ]. Pekala, buna biraz rastgelelik eklemeyi deneyelim .

Öncelikle, hash tablomuzun da bir tohum içerdiğini rve rinşa sırasında rastgele bir sayıya atandığını varsayalım . Bir kez atarız ve ardından bu karma tablo örneği için sabitlenir. Şimdi sözde kodumuzu tekrar gözden geçirelim.

function get(a: Table with m buckets and seed r, k: Key being looked up)
  rHash <- H[r]
  bucket <- compute rHash(k) modulo m
  for each (key,value) in a[bucket]
    return value if k == key
  return not_found

Meydan okumayı bir kez daha denersek: 1. adımdan itibaren rakip, sahip olduğumuz tüm hash fonksiyonlarını bilebilir H, ancak şimdi kullandığımız özel hash fonksiyonu buna bağlıdır r. Değeri rkendi yapımıza özeldir, rakip onu çalışma zamanında inceleyemez veya önceden tahmin edemez, bu yüzden bizim için her zaman kötü olan bir liste yapamaz. En adımda (2) hasım bir işlevi seçer olduğunu varsayalım hashiçinde Hrastgele, o da bir listesini el sanatları naltında çarpışmaları hash modulo mve gönderdiği zamanında o parmak kapısı adım (3), için H[r]aynı olacaktır hashonlar seçti.

Bu, rakip için ciddi bir bahis, hazırladığı liste altında çarpışıyor hash, ancak diğer hash işlevlerinin altında rastgele bir girdi olacak H. Bu bahsi kazanırsa, çalışma süremiz eskisi O(n)gibi en kötü durum olacak , ancak kaybederse, bize sadece ortalama O(1)süreyi alan rastgele bir girdi veriliyor . Ve gerçekten de çoğu zaman düşman kaybedecek, her |H|meydan okumayı yalnızca bir kez kazanır ve biz yapabiliriz.|H| çok büyük olabiliriz.

Bu sonucu, rakibin her zaman meydan okumayı kazandığı önceki algoritmayla karşılaştırın. Burada biraz el sallamak, ancak çoğu zaman düşman başarısız olacağından ve bu, düşmanın deneyebileceği tüm olası stratejiler için doğrudur, bunun sonucu olarak en kötü durum olsa da O(n), beklenen en kötü durum gerçektir O(1).


Yine, bu resmi bir kanıt değil. Bu beklenen en kötü durum analizinden aldığımız garanti, çalışma süremizin artık herhangi bir belirli girdiden bağımsız olmasıdır . Motive olmuş bir düşmanın kolayca kötü girdiler üretebileceğini gösterdiğimiz ortalama vaka analizinin aksine, bu gerçekten rastgele bir garantidir.


0

O (1) en kötü durum zamanlarını alabileceğiniz iki ayar vardır .

  1. Kurulumunuz statikse, FKS hashing işlemi size en kötü durumu O (1) getirecektir garantilerini . Ancak belirttiğiniz gibi ayarınız statik değil.
  2. Cuckoo hashing kullanırsanız, sorgular ve silmeler O (1) en kötü durumdur, ancak ekleme yalnızca O (1) beklenir. Guguk kuşu hash işlemi, toplam ek sayısı üzerinde bir üst sınırınız varsa ve tabla boyutunu kabaca% 25 daha büyük olacak şekilde ayarlarsanız oldukça iyi çalışır.

Buradan kopyalandı


0

Buradaki tartışmaya dayanıyor gibi görünüyor, eğer X (tablodaki öğelerin sayısı / bölmelerin sayısı) tavanıysa, bin aramasının verimli bir uygulamasını varsayarak daha iyi bir yanıt O (log (X)) olacaktır.


0

A. Değer, hash tablosunun boyutundan daha küçük bir int. Bu nedenle, değer kendi hash'idir, dolayısıyla hash tablosu yoktur. Ama olsaydı, O (1) olurdu ve yine de verimsiz olurdu.

Bu, anahtarları farklı gruplara önemsiz bir şekilde eşleyebileceğiniz bir durumdur, bu nedenle bir dizi, karma tablodan daha iyi bir veri yapısı seçimi gibi görünür. Yine de, verimsizlikler masa boyutuyla artmıyor.

(Hala bir karma tablo kullanabilirsiniz çünkü program geliştikçe int'lerin tablo boyutundan daha küçük kalacağına güvenmiyorsunuz, bu ilişki devam etmediğinde kodu potansiyel olarak yeniden kullanılabilir hale getirmek istiyorsunuz ya da yapmıyorsunuz İnsanların kodu okuyan / sürdüren kişilerin ilişkiyi anlamak ve sürdürmek için zihinsel çabayı boşa harcamalarını istemek).

B. Değerin bir karmasını hesaplamalısınız. Bu durumda, aranan verinin boyutu için sıra O (n) olur. O (n) işini yaptıktan sonra arama O (1) olabilir, ama bu yine de gözlerimde O (n) 'ye çıkıyor.

Anahtarın boyutu (örneğin bayt cinsinden) ve karma tabloda depolanan anahtar sayısının boyutu arasında ayrım yapmamız gerekir. Karma tabloların O (1) işlemleri sağladığı iddiaları, işlemlerin (ekle / sil / bul) anahtar sayısı arttıkça daha fazla yavaşlama eğiliminde olmadığı anlamına gelir. yüzlerden binlere, milyonlara ve milyarlarca (en azından tüm veriler RAM veya disk - önbellek efektleri devreye girebilir, ancak en kötü durumdaki önbellek kaçırmanın maliyeti bile en iyi durumda isabetin sabit katları olma eğilimindedir) eşit derecede hızlı depolamada erişilir / güncellenir.

Bir telefon rehberini düşünün: orada oldukça uzun isimler olabilir, ancak kitapta 100 veya 10 milyon isim olsa da, ortalama isim uzunluğu oldukça tutarlı olacak ve tarihteki en kötü durum ...

Bugüne kadarki en uzun isim için Guinness dünya rekoru, Adolph Blaine Charles David Earl Frederick Gerald Hubert Irvin John Kenneth Lloyd Martin Nero Oliver Paul Quincy Randolph Sherman Thomas Uncas Victor William Xerxes Yancy Wolfeschlegelsteinhausenbergerdorff, Senior

... wcbana bunun 215 karakter olduğunu söylüyor - bu anahtar uzunluğunun zor bir üst sınırı değil, ancak kitlesel olarak daha fazla olduğu konusunda endişelenmemize gerek yok .

Bu, çoğu gerçek dünya hash tablosu için geçerlidir: ortalama anahtar uzunluğu, kullanımdaki anahtarların sayısı ile artma eğiliminde değildir. İstisnalar vardır, örneğin, bir anahtar oluşturma yordamı artan tam sayılar içeren dizeler döndürebilir, ancak o zaman bile anahtarların sayısını bir büyüklük sırasına kadar artırdığınızda, anahtar uzunluğunu yalnızca 1 karakter artırırsınız: bu önemli değildir.

Sabit büyüklükteki anahtar verilerden bir karma oluşturmak da mümkündür. Örneğin, Microsoft'un Visual C ++, bir Standart Kitaplık uygulamasıyla std::hash<std::string>birlikte gelir, dizge boyunca eşit aralıklarla yerleştirilmiş yalnızca on baytı içeren bir karma oluşturur, bu nedenle dizeler yalnızca diğer dizinlerde değişiyorsa, çarpışmalar (ve dolayısıyla pratikte O (1) olmayan davranışlar elde edersiniz. çarpışma sonrası arama tarafında), ancak hash oluşturma zamanı sert bir üst sınıra sahiptir.

Ve mükemmel bir hash veya büyük bir hash tablonuz yoksa, muhtemelen kova başına birkaç öğe vardır. Böylece, bir noktada zaten küçük bir doğrusal aramaya dönüşür.

Genel olarak doğru, ancak karma tablolarla ilgili harika olan şey, bu "küçük doğrusal aramalar" sırasında ziyaret edilen anahtar sayısının - çarpışmalara ayrı zincirleme yaklaşımı için - karma tablo yük faktörünün (anahtarların kova oranı) bir fonksiyonudur.

Örneğin, 1.0'lık bir yük faktörü ile, anahtar sayısına bakılmaksızın, bu doğrusal aramaların uzunluğunun ortalama ~ 1.58'i vardır ( cevabıma buradan bakın ). İçin kapalı karma biraz daha karmaşık, ancak yük faktörü çok yüksek olmadığı zaman değil daha kötü.

Teknik olarak doğrudur çünkü anahtardaki tüm bilgileri kullanmak için hash fonksiyonuna gerek yoktur ve bu nedenle sabit zaman olabilir ve yeterince büyük bir tablo çarpışmaları neredeyse sabit zamana indirebilir.

Bu tür bir noktayı kaçırıyor. Her türlü ilişkisel veri yapısı nihayetinde bazen anahtarın her bölümünde işlem yapmak zorundadır (eşitsizlik bazen anahtarın sadece bir kısmından belirlenebilir, ancak eşitlik genellikle her bitin dikkate alınmasını gerektirir). En azından, anahtara bir kez karma yapabilir ve karma değerini depolayabilir ve yeterince güçlü bir karma işlevi kullanırsa - örneğin 64 bit MD5 - aynı değere (bir şirket Dağıtılmış veritabanı için tam olarak bunu yapmak için çalıştım: karma oluşturma süresi WAN genelindeki ağ iletimlerine kıyasla hala önemsizdi). Dolayısıyla, anahtarı işlemenin maliyeti konusunda çok fazla kafa yormaya gerek yok: bu, veri yapısından bağımsız olarak anahtarları saklamanın doğasında var ve yukarıda da belirtildiği gibi - değil '

Çarpışmaları azaltan yeterince büyük karma tablolara gelince, bu da önemli nokta eksik. Ayrı zincirleme için, herhangi bir yük faktöründe sabit bir ortalama çarpışma zinciri uzunluğuna sahip olursunuz - yük faktörü daha yüksek olduğunda ve bu ilişki doğrusal olmadığında sadece daha yüksektir. SO kullanıcısı Hans'ın cevabımla ilgili yorumu ayrıca yukarıda da bağlantılıdır :

Boş olmayan kepçelerde koşullandırılan ortalama kepçe uzunluğu daha iyi bir verimlilik ölçüsüdür. Bu a / (1-e ^ {- a}) [burada a yük faktörüdür, e 2.71828'dir ...]

Dolayısıyla, tek başına yük faktörü, ekleme / silme / bulma işlemleri sırasında aramanız gereken ortalama çarpışan anahtar sayısını belirler. Ayrı zincirleme için, sadece yük faktörü düşük olduğunda sabit olmaya yaklaşmaz - her zaman sabittir. Açık adresleme için, iddianızın bir miktar geçerliliği olsa da: bazı çarpışan öğeler, alternatif bölümlere yeniden yönlendirilir ve daha sonra diğer anahtarlardaki işlemlere müdahale edebilir, bu nedenle daha yüksek yük faktörlerinde (özellikle> .8 veya .9) çarpışma zinciri uzunluğu daha büyük ölçüde kötüleşir.

Pratikte doğrudur çünkü zamanla, hash işlevi ve tablo boyutu çarpışmaları en aza indirecek şekilde seçildiği sürece çalışır, ancak bu genellikle sabit bir zaman karma işlevi kullanmamak anlamına gelir.

Tablonun boyutu, yakın hashing veya ayrı zincirleme seçeneği verildiğinde mantıklı bir yük faktörüne yol açmalıdır, ancak hash işlevi biraz zayıfsa ve anahtarlar çok rastgele değilse, asal sayıda kova olması genellikle azaltmaya yardımcı olur çarpışmalar da ( hash-value % table-sizedaha sonra, karma değerde yalnızca yüksek dereceli bir bit veya ikiye yapılan değişiklikler, karma tablosunun farklı bölümlerine sözde rasgele dağılan kovalara çözülecek şekilde sarılır).

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.