TL; DR:O(1)
ハッシュ関数のユニバーサルファミリーからランダムにランダムにハッシュ関数を選択した場合、ハッシュテーブルは予想される最悪の場合の時間を保証します。予想される最悪のケースは平均的なケースと同じではありません。
免責事項:私はO(1)
、ハッシュテーブルがであることを正式には証明していません。そのため、コースラ[ 1 ]のこのビデオをご覧ください。私はまた、償却された、ハッシュテーブルの面。これは、ハッシュと衝突についての議論に直交しています。
他の回答やコメントでこのトピックに関する驚くほど多くの混乱が見られますが、この長い回答でそれらのいくつかを修正しようとしています。
最悪のケースについての推論
ワーストケース分析にはさまざまなタイプがあります。これまでのところ、ほとんどの回答がこれまでに行った分析は、最悪のケースではなく、平均的なケースです [ 2 ]。平均的なケース分析の方が実用的です。多分あなたのアルゴリズムには悪い最悪の場合の入力が1つありますが、実際には他のすべての可能な入力に対してうまく機能します。結論は、ランタイムは実行しているデータセットに依存するということです。
get
ハッシュテーブルのメソッドの次の疑似コードを考えます。ここでは、連鎖によって衝突を処理することを想定しているため、テーブルの各エントリは、リンクされた(key,value)
ペアのリストです。また、バケットの数m
は固定されていると仮定します。O(n)
ただしn
、は入力の要素数です。
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
他の回答が指摘しているように、これは平均O(1)
して最悪のケースで実行されO(n)
ます。ここで挑戦することで証明の小さなスケッチを作ることができます。課題は次のとおりです。
(1)ハッシュテーブルアルゴリズムを敵に渡します。
(2)敵はそれを研究し、彼が望む限り準備することができます。
(3)最後に、敵はあなたn
にあなたのテーブルに挿入するためのサイズの入力を与えます。
問題は次のとおりです。あなたのハッシュテーブルは敵の入力でどれくらい速いですか?
ステップ(1)から、攻撃者はハッシュ関数を知っています。ステップ(2)の間に、敵対者は、例えば要素の束のハッシュをランダムに計算することによりn
、同じ要素を持つ要素のリストを作成できhash modulo m
ます。そして(3)で彼らはあなたにそのリストを与えることができます。しかし、驚いたことに、すべてのn
要素が同じバケットにハッシュされるため、アルゴリズムはO(n)
そのバケット内のリンクされたリストをトラバースするのに時間がかかります。何度チャレンジを試みても、敵は常に勝利しますO(n)
。それが、最悪の場合、アルゴリズムがどれほど悪いかです。
ハッシュはO(1)になるのはなぜですか?
以前の課題で私たちを驚かせたのは、攻撃者がハッシュ関数を非常によく知っていて、その知識を使用して最悪の可能な入力を作成できることでした。常に1つの固定ハッシュ関数を使用する代わりにH
、実行時にアルゴリズムがランダムに選択できる一連のハッシュ関数が実際にあるとしたらどうでしょうか。気になる人のために、ハッシュ関数のユニバーサルファミリH
と呼ばれています [ 3 ]。さて、これにランダム性を追加してみましょう。
まず、当社のハッシュテーブルはまた、種子を含んと仮定r
し、r
構築時にランダムな番号に割り当てられています。一度割り当てると、そのハッシュテーブルインスタンスに対して固定されます。今度は、疑似コードをもう一度見てみましょう。
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
もう一度チャレンジしてみると、ステップ(1)から、攻撃者はのすべてのハッシュ関数を知ることができますがH
、使用する特定のハッシュ関数はに依存していr
ます。の値r
は私たちの構造にプライベートであり、攻撃者は実行時にそれを検査することも、事前に予測することもできないため、私たちにとって常に悪いリストを作成することはできません。ステップ(2)でランダムに1つの関数hash
を選択した攻撃者がH
、でn
衝突のリストhash modulo m
を作成し、ステップ(3)でそれを送信H[r]
すると、実行時hash
に選択したものと同じになるように指を交差するとします。
これは敵対者にとって深刻な賭けです。彼が作成したリストはで衝突しhash
ますが、の他のハッシュ関数ではランダム入力になりH
ます。彼がこの賭けに勝った場合、ランタイムはO(n)
以前と同様に最悪のケースになりますが、負けた場合、平均O(1)
時間をとるランダムな入力が与えられるだけです。そして実際、ほとんどの場合、敵は負け、|H|
挑戦ごとに一度だけ勝ち、私たちは|H|
非常に大きくすることができます。
この結果を、敵が常に挑戦に勝った以前のアルゴリズムと比較してください。ここで少し手を振っていますが、ほとんどの場合、敵対者は失敗し、これは敵対者が試すことができるすべての可能な戦略に当てはまるため、最悪のケースはですO(n)
が、予想される最悪のケースは実際O(1)
です。
繰り返しますが、これは正式な証明ではありません。この予想される最悪のケースの分析から得られる保証は、ランタイムが特定の入力から独立していることです。これは真にランダムな保証であり、意欲的な敵が悪い入力を簡単に作成できることを示した平均的なケース分析とは対照的です。