web-dev-qa-db-ja.com

私はハッシュテーブルを理解しようとしています-誰かがそれを私に説明できますか?

PHPでのハッシュテーブルの正しい使用と実装を理解したい(申し訳ありません)。

どこかで、経験の浅いプログラマーがハッシュテーブルを作成し、それを繰り返し処理したことを読みました。今、私はそれが間違っている理由を理解していますが、私の理解が正しいかどうか(私が何を意味するかを知っている場合)を知るための完全な知識がまだありません。

誰かが私に、PHPでハッシュテーブルを実装する方法(おそらく連想配列)を説明できますか?多分もっと重要なのは、「ハッシュで」値にアクセスする方法とそれが実際に何を意味するのでしょうか?

25
Stevo

単純なハッシュテーブルの概要

復習として、ハッシュテーブルは、データ構造の特定のキーの下に値を格納する方法です。たとえば、値"a"をキー1の下に格納し、後でハッシュテーブルでキー1を検索して取得できます。

私の頭の上で考えることができるハッシュテーブルの最も簡単な例は、整数のみを格納できるハッシュテーブルで、ハッシュテーブルエントリのキーは格納される値でもあります。テーブルのサイズが8で、基本的にはメモリ内の配列だとします。

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

ハッシュ関数

ハッシュ関数は、値を格納する場所のインデックスを提供します。このテーブルの非常に単純なハッシュ関数は、格納する値に1を加算し、次に mod 8(テーブルサイズ)を加算することです。つまり、ハッシュ関数は(n+1)%8であり、nは格納する整数です。

インサート

このハッシュテーブルに値を挿入する場合は、挿入する値に対してハッシュ関数(この場合は(n+1)%8)を呼び出して、インデックスを作成します。たとえば、14を挿入する場合は、(14 + 1) % 8を呼び出してインデックス7を取得するので、インデックス7に値を挿入します。

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

同様に、33、82、191を次のように挿入できます。

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

衝突

しかし、エントリと衝突するようなものを挿入しようとするとどうなりますか? 2はインデックス3に入るはずですが、82で取得されます。この問題を解決するには複数の方法があります。最も簡単な方法は、空のスペースが見つかるまでハッシュ関数を何度も繰り返し呼び出すことです。

したがって、ロジックは次のとおりです。

  1. (2 + 1)%8 =3
  2. インデックス3がいっぱいです
  3. 3をハッシュ関数に戻します。 (3+ 1)%8 =4、これは空です。
  4. 値をインデックス4に配置します。

ハッシュテーブルは次のようになり、値2がインデックス4に格納されます。

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

このソリューションの欠点は、すぐにテーブルがいっぱいになることです。データサイズが制限されていることがわかっている場合、テーブルがすべての可能な値を保持するのに十分な大きさであれば、これは問題になりません。もっと多く保持できるようにしたい場合は、衝突を別の方法で処理できます。 2を挿入する前の状態に戻りましょう。

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

覚えているとしたら、(2+1)%8は、インデックス3を取得します。ハッシュテーブルをいっぱいにしたくない場合は、各テーブルインデックスをリンクリストとして使用し、そのインデックスでリストに追加できます。したがって、ハッシュ関数を再度呼び出す代わりに、インデックス3のリストに単に追加します。

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

このリストは、メモリが許す限り大きくすることができます。 18を挿入でき、2に追加されます。

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

ルックアップ

ハッシュテーブルのサイズがかなり大きい場合は、ハッシュテーブルの値をすばやく検索できます。ハッシュ関数を呼び出して、インデックスを取得するだけです。 82がテーブルにあるかどうかを確認するとします。ルックアップ関数は(82+1)%8 = 3を呼び出し、インデックス3の項目を確認して返します。 16を検索した場合、検索関数はインデックス1を検索し、それが存在しないことを確認します。

ルックアップも衝突を処理する必要があります!

値2を検索しようとすると、ハッシュテーブルでは、データの取得と同じ衝突ロジックを使用して、データを取得する必要があります。ハッシュテーブルの動作方法に応じて、探しているエントリが見つかるまで(または空白スペースが見つかるまで)キーを何度もハッシュするか、アイテムが見つかるまでリンクリストを反復処理します(またはリストの最後に到達した)

概要

したがって、ハッシュテーブルは、キーと値のペアをすばやく格納してアクセスするための優れた方法です。この例では、値と同じキーを使用しましたが、実際のハッシュテーブルでは、キーはそれほど制限されていません。ハッシュ関数はキーに作用してインデックスを生成し、キー/値をそのインデックスに格納できます。ハッシュテーブルは、反復することは可能ですが、実際に反復することを意図していません。ご覧のとおり、ハッシュテーブルには多数の空白スペースが含まれる可能性があり、それらを繰り返し処理することは時間の無駄になります。ハッシュテーブルの反復子で空白の検索をスキップするロジックがある場合でも、リンクリストなどの反復子用に設計されたデータ構造を使用する方が適しています。

38
Jeff

何千冊もの本がある図書館を想像してみてください。書籍を整理して、タイトルごとにできるだけ早く見つけられるようにする必要があります。

これを行う(一般的な)方法の1つは、本をアルファベット順に並べ替えることです。タイトルが「G」で始まる場合は、「G」エリアを見つけ、2番目の文字を探し、「ö」、次に「d」、「e」、「l」と検索し、検索を絞り込みます。 、あなたが本を見つけるまで。ただし、これには長い時間がかかるだけでなく、新しい本が届いたときにレイアウトを再編成して、新着本のためのスペースを空ける必要がある場合があります。

それは二分探索です。それは良いです。

ただし、これを行うためのより速い方法があります。すべての本棚と棚を列挙し、各本について、本が見つかるはずの本棚/棚に対応する特別な、できればユニークな数値を計算するとします。 「キー」を計算する方法は、ランダムに見える数値を与える限り、それほど重要ではありません。たとえば、タイトルのすべての文字の文字コードを追加し、それを素数で除算することができます(おそらく最良の方法ではありませんが、とにかく機能します)。

それはハッシュです。本棚や棚全体を調べてタイトルの次の文字を探す必要がないため、はるかに速くなります。 2つ以上の本が同じキーに解決されるときに「衝突」がない限り、ハッシュは通常、1回限りの操作です。しかし、それは問題ありません。それらは互いに隣り合っており、ハッシュ関数の品質によっては、同じキーの下に多すぎてはいけません。

ハッシュテーブルにはいくつかの制限と気まぐれ(再ハッシュ/サイズ変更)があり、実行可能な競合相手としてバイナリ検索を維持します。どちらの方法が優れているかについては、すべてが白黒というわけではありません。しかし、それは別の話です。

追伸質問に直接回答しないで申し訳ありません(PHPでハッシュテーブルを記述します)が、それは詳細であり、「プログラミング」と呼ばれています;)

7
mojuba

PHPのハッシュテーブルは、私の知る限り、次の方法で簡単に実装できます。

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

次に、次のような呼び出しを介してデータにアクセスします。

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

Foreach()関数を使用して、配列の内容を反復処理します。

ハッシュテーブルを理解する最良の方法は http://en.wikipedia.org/wiki/Hash_table のようなものを読むことですが、おおまかに言えばこれに要約されます:その中のすべての行の左側array()呼び出しがキーです。これらのキーはハッシュ計算にかけられ、結果はハッシュになります。おそらくMD5またはSHAハッシュを以前に見たことがあります。これはこれに非常に似ています。このハッシュの特定の部分、通常最初のX文字ですが、完全なハッシュは、いわゆる「バケット」。値の格納領域です(右側)。

次に、ハッシュテーブルにアクセスするときはいつでも、キーを使用して値を取得します。キーは再びハッシュに対して計算され、そのハッシュは関連する値をすばやく検索するために使用されます。したがって、ハッシュテーブルを使用すると、すべてが単に格納されている場合に、リナリーを検索するよりも高速に検索できます。唯一の欠点は、いくつかのハッシュ実装が衝突に悩まされることです。これは、2つの異なるキーに対して計算された同じハッシュです。一般に、それほど心配する必要はありません。

これである程度の背景が得られることを願っていますが、興味がある場合は、この件についてもっと読んでみてください。私の説明は非常に初歩的であり、そこには十分な穴があると確信していますが、簡単な説明にはそれで十分です。

1
asmodai