Hash tablolarını anlamaya çalışıyorum - birisi bana açıklayabilir mi - açıkça?


25

Hash tablolarının php (doğru) olarak doğru kullanımı ve uygulanmasını anlamak istiyorum.

Bir yerde, deneyimli bir programcının bir karma tablo oluşturduğunu ve daha sonra yineleme yaptığını okudum. Şimdi, bunun neden yanlış olduğunu anlıyorum ancak anlayışımın doğru olup olmadığını (ne demek istediğimi anlıyorsanız) bilecek tam bilgiye sahip değilim.

Öyleyse birileri bana bir hash tablosunun php (muhtemelen bir ilişkisel dizi) içinde nasıl uygulanacağını ve belki de daha önemlisi, “karma” değerlerine nasıl erişileceğini ve bunun ne anlama geldiğini açıklayabilir mi?

Yanıtlar:


37

Basit Karma Tabloya Genel Bakış

Tazeleme olarak, karma tablo, bir veri yapısındaki belirli bir anahtarın altındaki bir değeri saklamanın bir yoludur. Örneğin "a", anahtarı anahtarın altında saklayabilir 1ve daha sonra 1hash tablosundaki anahtarı arayarak alabilirim .

Kafamın üstünden düşünebildiğim en basit tablo, sadece tamsayıları saklayabilen bir karma tablodur; buradaki karma tablo girişi için anahtar aynı zamanda depolanan değerdir. Diyelim ki masanız 8 beden ve temelde bellekteki bir dizi:

---------------------------------
|   |   |   |   |   |   |   |   |
---------------------------------
  0   1   2   3   4   5   6   7  

Özet fonksiyonu

Karma işlevleri, değerinizi nereye kaydedeceğinize dair bir dizin verir. Bu tablo için oldukça basit bir hash fonksiyonu saklamak istediğiniz değere 1 ekleyin ve sonra olacaktır mod 8 (tablo boyutu) bunu. Başka bir deyişle, karma fonksiyonudur (n+1)%8, nerede nsaklamak istediğiniz tam sayıdır.

ekler

Bu karma tabloya bir değer (n+1)%8eklemek istiyorsanız, size bir dizin vermek için eklemek istediğiniz değer üzerindeki karma işlevinizi çağırın (bu durumda ). Mesela, eğer 14 eklemek istiyorsak, (14 + 1) % 8index'i çağırır ve 7alırdık, böylece onu index'e eklerdik 7.

---------------------------------
|   |   |   |   |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Benzer şekilde, 33, 82 ve 191 gibi ekleyebilirsiniz:

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Çarpışmalar

Ancak bir girişle çarpışacak bir şey eklemeye çalışırsak ne olur? 2 endekse girmelidir 3, ancak 82 tarafından alınmaktadır. Bu sorunu çözmek için birden fazla yol vardır, en basit olanı boş bir boşluk bulana kadar tekrar tekrar karma fonksiyonunu çağırmaktır.

Yani mantık aşağıdaki gibidir:

  1. (2 + 1)% 8 = 3
  2. Dizin 3 dolu
  3. Hash fonksiyonumuza tekrar 3 takın . ( 3 + 1)% 8 = 4 , boş.
  4. Değerimizi dizin 4'e yerleştirin .

Şimdi, karma tablo şöyle görünür, 2 değeri indekste saklanır 4.

---------------------------------
|191|   |33 |82 |2  |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Bu çözümün dezavantajı, çok yakında, masamızın dolması! Veri boyutunuzun sınırlı olduğunu biliyorsanız, tablonuzun tüm olası değerleri tutacak kadar büyük olması koşuluyla bu sorun olmamalıdır. Daha fazla tutabilmek istiyorsanız, çarpışmaları farklı şekilde ele alabilirsiniz. 2 eklemeden önce bulunduğumuz yere geri dönelim.

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Hatırlarsanız, alınan (2+1)%8indeksi bize verir 3. Karma tablonuzun dolmasını istemiyorsanız, her tablo endeksini bağlantılı liste olarak kullanabilir ve o dizindeki listeye ekleyebilirsiniz. Böylece tekrar karma işlevini çağırmak yerine, sadece indeksindeki listeye ekleyeceğiz 3:

            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Bu liste daha sonra hafızanın izin verdiği kadar büyüyebilir. 18 ekleyebilirim, ve sadece 2 eklenir:

            -----
            |18 |
            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

aramaları

Karma tablonuzdaki arama değerleri, karma tablonuz oldukça büyük bir boyutta olduğu için hızlıdır. Sen sadece hash fonksiyonunu çağır ve dizini al. Diyelim ki 82 masanızda mı görmek istiyorsunuz. Arama işlevi (82+1)%8= 3öğesini çağırır ve dizindeki öğeye bakar 3ve sizin için döndürür. Eğer 16'yı aradıysanız, arama fonksiyonu indekse bakar 1ve var olmadığını görür.

Aramalar Çarpışmalarla da Başa Çıkmanız Gerekiyor!

2 değerini aramaya çalışırsanız, karma tablonuz, verileri almak için olduğu gibi verileri depolamak için kullanılan aynı çarpışma mantığını kullanmak zorunda kalır. Karma tablonuzun çalışma biçimine bağlı olarak, aradığınız girişi bulana (veya boş bir yer) bulana kadar anahtarı tekrar tekrar basılı tutarsınız ya da öğeyi bulana kadar bağlantılı listenizde yinelenirsiniz (veya listenin sonuna vardım)

özet

Dolayısıyla, karma tablolar anahtar / değer çiftlerini hızlı bir şekilde depolamak ve erişmek için iyi bir yoldur. Bu örnekte değer ile aynı anahtarı kullandık, ancak gerçek dünyada karma tablolarda anahtarlar çok sınırlı değil. Karma işlevleri, bir dizin oluşturmak için tuşlar üzerinde çalışır ve ardından anahtar / değer bu dizinde saklanabilir. Hash tabloları gerçekten yinelenmek için tasarlanmamıştır, ancak yapılması mümkündür. Gördüğünüz gibi, karma tabloların çok fazla boş alanı olabilir ve bunların arasında yineleme yapmak zaman kaybı olur. Karma tablo yinelemesinde boş alan aramalarını atlamak için bir mantık olsa bile, bağlantılı listeler gibi yineleyiciler için tasarlanmış bir veri yapısı kullanmak daha uygun olur.


2
ASCII sanat FTW!
Anto

2
Mükemmel cevap. Her bir dizinin bağlantılı bir liste olduğu yönteme zincirleme denildiğini belirtmekte fayda var.
alexn

+1 Mükemmel cevap, kafamdaki hemen hemen her şüphe ortaya çıktı. Bir soru daha sormam gerekiyor. Her uygulama tamsayıları saklamak için karma kullanıyor mu? ya da bu özel durumlar için kullanılıyor mu? eğer evet ise, o zaman bu davalar nelerdir?
0decimal0

@PHIfounder Sorunuzu tamamen anladığımdan emin değilim, ancak anahtar üzerinde gerçekleştirilen karma işlevi, yalnızca tamsayılar gibi belirli bir veri türüne uygulamak için değil genel olarak tasarlanmıştır. C kodundan bahsediyorsak, karma tablosu anahtar ve değer için kabul etmek (geçersiz *) ve anahtarın işaretçi değerinde bir karma hesaplama yapmak için tasarlanabilir.
Jeff,

@Jeff aslında bunu sormak için aptal olabilirim, ancak bilgisayarın iç yapısından bahsediyorum; Her bilgisayarın deposunu saklamak için karma tablo gibi bir veri yapısı kullanıp kullanmadığı, tamsayılara başvuruyor mu yoksa dahili olarak değil mi?
0decimal0

7

Binlerce kitap içeren bir kütüphane hayal edin. Kitapları, başlıklara göre olabildiğince çabuk bulabilmek için düzenlemelisiniz.

Bunu yapmanın (ortak) bir yolu, kitapları alfabetik olarak sıralamaktır. Başlığınız "G" ile başlıyorsa, "G" alanını bulursanız, ikinci harfi arayın, "ö" deyin, sonra "d", "e", "l", aramanızı daraltın, vb. , kitabı bulana kadar. Ancak bu, uzun sürebilir ve ayrıca, yeni kitaplar geldiğinde bazen yeni gelenler için yer açmak üzere düzeninizi yeniden düzenlemeniz gerekir.

Bu ikili arama. Bu iyi.

Bununla birlikte, bunu yapmanın daha hızlı bir yolu var. Diyelim ki tüm kitaplık ve rafları sayıyorsunuz ve ardından her kitap için kitabın bulunması gereken bir kitaplık / rafa eşlenen özel, umarım eşsiz bir sayı hesaplıyorsunuz. "Anahtar" ı hesaplama şekliniz rastgele görünen bir sayı verdiği sürece önemli değildir. Örneğin, başlıktaki tüm harflerin karakter kodlarını ekleyebilir ve ardından bazı asal sayılara bölebilirsiniz (muhtemelen en iyi yöntem değil, yine de çalışır).

Bu karmaşa. Çok daha hızlı, çünkü başlıktaki bir sonraki mektuba bakarak tüm kitaplıklardan ve raflardan geçmeniz gerekmez. İki veya daha fazla kitap aynı anahtara çözümlendiğinde bir "çarpışma" olmadıkça, karma, genellikle tek seferlik bir işlemdir. Fakat sorun değil, yan yana yattıklarını biliyorsunuz ve karma fonksiyonunun kalitesine bağlı olarak, aynı anahtarın altında çok fazla olmamalı.

Karma tablolarda, ikili aramayı geçerli bir rakip olarak tutan bazı sınırlamalar ve hevesler vardır (yeniden şekillendirme / yeniden boyutlandırma). Hangi yöntemin daha iyi olduğuna göre hepsi siyah beyaz değil. Ama bu farklı bir hikaye.

PS Sorunuzu doğrudan yanıtlayamadığım için üzgünüm (PHP'de bir karma tablo yazın), ancak bu ayrıntılara "programlama" denir;)


2
Bilgisayarla ilgili olmayan problemleri bilgisayarla ilgili olmayan açıklamaları severim. +1
gablin

1

PHP'deki hash tablosu, bildiğim kadarıyla, basit bir şekilde:

$my_hash = array(
    1 => "Bob",
    2 => "Alice",
    3 => "Jack"
);

Daha sonra aşağıdaki gibi aramalar yoluyla verilere erişin:

echo $my_hash[2]; // Will echo "Alice"

Dizinin içeriğini yinelemek için foreach () işlevini kullanırsınız.

Karma tabloları anlamanın en iyi yolu, http://en.wikipedia.org/wiki/Hash_table gibi bir şey okumaktır , ancak kabaca bu kaynar: bu dizi () çağrısının içindeki her satırın sol tarafı anahtarlardır . Bu anahtarlar bir karma hesaplama yoluyla alınacak ve sonuç bir karma olacaktır. Muhtemelen daha önce MD5 veya SHA karmalarını gördünüz, buna oldukça benziyor. Bu hash'ın belirli bir kısmı, tipik olarak ilk X karakterleri, ancak bazen tam hash, değerlerin saklama alanları olan 'kovaları' tanımlamak için kullanılacaktır (sağ taraf).

Sonra ne zaman karma değerinize erişirseniz, değeri elde etmek için anahtarı kullanın. Anahtar bir karma değerine tekrar hesaplanır ve karma, ilgili değeri hızlıca aramak için kullanılır. Dolayısıyla, karma tablolar, her şey henüz kaydedildiyse, sadece doğrusallığı aramaktan daha hızlı bir görünüm sağlar. Tek dezavantajı, bazı karma uygulamaların iki farklı anahtar için aynı hesaplanmış karma olan çarpışmalardan muzdarip olmasıdır. Genel olarak, çok endişelenmen gereken bir şey değil.

Umarım bu biraz arka plan sağlar, ancak konuyla ilgileniyorsanız lütfen konuyla ilgili daha fazla okumaya çalışın. Açıklamam çok basit ve orada yeterince delik bulunduğundan eminim ama hızlı bir açıklama için yeterli olmalı.

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.