ハッシュテーブルO(1)はハッシュ速度をどのように考慮していますか?


8

ハッシュテーブルは、特定の容量で言う単純な連鎖と倍加を使用してを償却すると言われています。Θ(1)

ただし、これは要素の長さが一定であることを前提としています。要素のハッシュを計算するには、要素を、時間がかかります。ここで、は長さです。lΘ(l)l

ただし、要素を区別するには、少なくともビットの長さの要素が必要です。そうでなければ、鳩の巣の原理によって、それらは区別されません。要素のビットを通過するハッシュ関数は時間かかります。LG N LG N Θ LG N nlgnlgnΘ(lgn)

代わりに、入力のすべての部分を使用する合理的なハッシュ関数を考慮したハッシュテーブルの速度は、実際にはであると言えるでしょうか。では、なぜ実際にハッシュテーブルが文字列や大きな整数などの可変長要素を格納するのに効率的であるのでしょうか。Θ(lgn)



4
答えはそうではありません。このタイプのハッシュ分析では、要素の次元(またはビット数)は考慮されず、要素の数のみが考慮されます。
Nikos M.

しかし、前述のビットの読み取りと書き込みを考慮しないハッシュマップルックアップがである場合、同じ基準で、バイナリ検索またはその他のプロセス通常、 は実際にはなると考えてください。Θ リットルG N ΘのリットルG N Θ L G 2 N Θ(1)Θ(lg n)Θlg nΘ(lg2 n)


@tAllan通常のバイナリ検索はが、キーのビットシーケンスに従って項目をソートし続け、「一度に1ビット」を比較するバイナリ検索を実行すると(トリッキーな詳細は省略)、を達成できる。Θ log n Θ(log2n)Θ(logn)
モニカ

回答:


3

ハッシュテーブルが償却されるという話は、過度に単純化した嘘です。 Θ(1)

場合にのみ真である:
-アイテムごとのハッシュへのデータの量が数と比較して自明であるKの EYSとハッシュの速度Kの EYは速い-。 -多くのCの ollisionsが小さい-。 -私たちは、ないではないに必要なアカウントの時間を考慮するRハッシュテーブルをESIZE - Rをck
c
r

ハッシュする大きな文字列
最初の仮定がfalseの場合、実行時間はまで増加します。 これは確かに大きな文字列にも当てはまりますが、大きな文字列の場合、単純な比較ではΘ k )の実行時間になります。したがって、ハッシュは漸近的に遅くなるわけではありませんが、比較は初期のオプトアウトergo O 1 Ω k を持ち、ハッシュは常に完全な文字列O k をハッシュする必要があるため、ハッシュは常に単純な比較よりも遅くなります。Ω k Θ(k)
Θ(k)O(1)Ω(k)O(k)Ω(k)

整数は非常にゆっくりと増加することに注意してください。8バイトは最大値を格納できます。8バイトは、ハッシュに取るに足らない量です。 bigintを保存したい場合は、それらを文字列と考えてください。 1018

スローハッシュアルゴリズムハッシュに
費やす金額がデータのストレージと比較して重要な場合、明らかに仮定は受け入れられなくなります。 暗号化ハッシュを使用しない限り、これは問題にはなりません。Θ(1)

重要なのはそれである> > K。それが保持している限りとしてΘ 1 公正な文です。n >> kΘ(1)

多くの衝突
ハッシュ関数が貧弱な場合、またはハッシュテーブルが小さい場合、またはハッシュテーブルのサイズが不自然な場合、衝突が頻繁に発生し、実行時間がます。 ハッシュ関数は、低速のハッシュを犠牲にして衝突の数を減らすことを疑う場合に、可能な限り高速でありながら衝突がまれになるように選択する必要があります。 経験則では、ハッシュテーブルの使用率は常に75%未満である必要があります。 また、ハッシュテーブルのサイズは、ハッシュ関数と相関関係があってはなりません。 多くの場合、ハッシュテーブルのサイズは(比較的)素数です。 O(log(n))



ハッシュテーブルのサイズ変更
ほぼ完全なハッシュテーブルでは衝突が多すぎ、大きな(空の)ハッシュテーブルはスペースの無駄になるため、多くの実装では、必要に応じてハッシュテーブルを拡大(および縮小)できます。
パフォーマンス上の理由からストレージを継続する必要があるため、テーブルの拡大には、すべてのアイテムの完全なコピー(および場合によっては再シャッフル)が含まれる可能性があります。
病理学的な場合にのみ、ハッシュテーブルのサイズ変更が問題になるため、(コストはかかりますがまれに)サイズ変更は多くの呼び出しにわたって償却されます。

実行時間
したがって、ハッシュテーブルの実際の実行時間はです。平均 でkcrのそれぞれは、償却実行時間の(小さい)定数であると想定されるため、Θ 1 は公平なステートメントであると言えます。 Θ(kcr)
kcrΘ(1)

質問に戻るには、言い訳を失礼に失礼します。
意味の異なるセットを抽出しようとしましたが、いくつか見逃した場合はコメントしてください

ハッシュ関数の出力の長さが気になるようです。これをとしましょう(nは通常、ハッシュするアイテムの数と見なされます)。mはハッシュテーブルのエントリを一意に識別する必要があるため、l o g n になります。 これは、mの成長が非常に遅いことを意味します。64ビットでは、ハッシュテーブルエントリの数が、世界中で利用可能なRAMのかなりの部分を占めます。128ビットでは、地球上で利用可能なディスクストレージをはるかに超えます。 128ビットハッシュの生成は32ビットハッシュよりも難しくないので、いいえ、ハッシュを作成する時間はO mnmlog(n)

(または、 O l o g n )。 O(m)O(log(n))

要素のビットを通過するハッシュ関数は、時間かかります。 log(n)Θ(log(n))

しかし、ハッシュ関数は要素のビットを通過しませ。 1アイテム(!!)ごとにデータのみを通過します。 また、入力の長さ(k)は要素の数とは関係ありません。これは重要です。一部の非ハッシュアルゴリズムでは、コレクション内の多くの要素を調べて(一致しない)一致する要素を見つける必要があるためです。 ハッシュテーブルは、結論に達する前に、平均して検討中の項目ごとに1つまたは2つの比較のみを実行します。 log(n)
O(k)

なぜハッシュテーブルは可変長要素を格納するのに効率的ですか?

入力の長さ()に関係なく、出力の長さ()は常に同じであるため、衝突はまれであり、ルックアップ時間は一定です。 ただし、キーの長さがハッシュテーブル内のアイテム数()に比べて大きくなると、ストーリーは変化します...km
kn

ハッシュテーブルはなぜ大きな文字列を格納するのに効率的ですか?

ハッシュテーブルは、非常に大きな文字列に対してあまり効率的ではありません

場合(つまり、入力のサイズがハッシュテーブルの項目数と比べてかなり大きい場合)、ハッシュの実行時間が一定であるとは言えませんが、実行時間に切り替える必要があります特に早期終了がないためです。完全なキーをハッシュする必要があります。限られた数のアイテムのみを保存する場合は、ソートされたストレージを使用する方がはるかに良いでしょうを比較すると、違いが見られ次第、オプトアウトできます。 not n>>kΘ(k)k1 k2

ただし、データがわかっている場合は、キー全体をハッシュするのではなく、(既知または想定される)揮発性部分のみをハッシュして、衝突をチェックしながらプロパティを復元することを選択できます。 Θ(1)

非表示の定数
誰もが知っておくべきことですが、は、処理される要素ごとの時間が一定であることを単に意味します。この定数は、単純な比較よりもハッシュの方がかなり大きいです。 小さなテーブルの場合、たとえば10のバイナリ比較は単一のハッシュよりも高速になる可能性があるため、バイナリ検索はハッシュルックアップよりも高速になります。 小さなデータセットの場合、ハッシュテーブルの代替を検討する必要があります。 ハッシュテーブルが本当に優れているのは、大規模なデータセットにあります。Θ(1)



定義がわかりません。サイズを変更すると、償却後の実行時間が長くなるとは限りません。適切にサイズ変更を行う限り、コピーのコストは償却でき、償却ランタイムは増加しません。ハッシュの速度が問題になることはないと思います(暗号化ハッシュでさえ非常に高速です。いずれにしても、入力の長さが定数によって制限されている場合、ハッシュは一定の時間で実行されます)。(衝突が少数になるので)、ランタイムの主張は、常に良いハッシュ関数を使用して上の偶発的です。k,c,rO(1)
DW

1
だから、あなたが言及した問題のうち、私は入力の長さだけが本当に深刻な問題だと思います。また、これは実際に尋ねられた質問に答えるものではありません。質問は出力の長さについて話し、出力の長さはビットではなくビットであると考えるのが最善です。それは正しいですが、見落としているのは、実行時間の計算に使用される計算モデルです。この答えはそれのいずれにも当てはまらないようですので、これが質問で提起された問題に達しているかどうかはわかりません。Ω(lgn)O(1)O(1)
DW

ランタイムのすべての要素を完全にしたかったのです。ハッシュするときは、キーの長さだけが本当に問題であることに同意します。OPで発生したlog(n)の問題を修正しました。IMOをハッシュするときはそれほど問題ではないので、私はそれを誤解しています。
ヨハン

答えがOPの質問と一致することを願っています。
ヨハン

3

簡単な質問から始めましょう。おそらく最も単純なデータ構造である配列について考えてみましょう。具体的には、整数の配列を想像してみましょう。の操作にはどのくらい時間がかかりますか?答えは計算モデルによって異なります。ここでは2つのモデルが関連しています。RAMモデル(より一般的)とビットモデル(説明が簡単)です。A[i]=A[j]

ではビットモデル、関連する基本的な操作ビットは、コスト。したがって、整数がビット幅の場合、演算は約コストがかかります。NNwA[i]=A[j]2w

RAMモデル、データの基本単位は、ビットではなく、単語(時にはとして知られている機械語)。ワードは、幅整数です。ここで、は入力のサイズ(ビット単位)です。関連する基本的な操作言葉はコスト。ほとんどの場合、整数配列の場合、必要な整数の幅はになるため、演算コストはます。lognnNNO(logn)A[i]=A[j]O(1)

上で述べたように、通常はRAMモデルを使用してアルゴリズムを分析します。唯一の一般的な例外は、整数演算、特に整数乗算です。これは、ビット演算の数に関して分析されることがよくあります。

なぜRAMモデルを使用するのですか?それは(現実に対して)より多くの予測力を持っているので。特に最新の64ビットプロセッサの場合、入力サイズがマシンワードのサイズで最大でも指数関数的であるという仮定は通常正当化され、実際のCPUではマシンワードの操作に一定の時間がかかります。


ハッシュテーブルはより複雑なデータ構造であり、実際にはキータイプ、ハッシュタイプ、値タイプの3つのタイプが関係しています。値のタイプの観点から見ると、ハッシュテーブルは美化された配列にすぎないので、その側面は無視しましょう。ハッシュタイプは、常に機械語の数が少ないからなると仮定することができます。キー型満たす特殊プロパティ:それはハッシュ可能それはいくつかの決定的関数(常に同じ値を返す関数)である(最小で)ハッシュ演算を有する手段は、。

私たちはあなたの質問に取り組むことができます:キーをハッシュするのにどれくらい時間がかかりますか?答えは計算モデルによって異なります。今回は、3つの一般的なモデルがあります。以前の2つとoracleモデルです。

ではオラクルモデル、我々は、ハッシュ関数は、一定時間内に任意のキーのハッシュを計算することができ、「神託」によって私たちに与えられていることを前提としています。

RAMモデルビットモデル、ハッシュ関数は、実際の関数であり、ハッシュテーブルの時間計算は、ハッシュ関数の時間複雑さに依存します。(暗号化の目的ではなく)ハッシュテーブルに使用されるハッシュ関数は、通常、非常に高速で、入力に線形の時間がかかります。つまり、キータイプの長さがビット(ビットモデルの場合)またはワード(RAMモデルの場合)である場合、ハッシュ関数には時間がかかります。とき一定で、ハッシュ関数は、一定の時間がかかります。NNO(N)N

ハッシュテーブルアルゴリズムの実行時間を分析する場合、通常、暗黙的にoracleモデルを使用します。これはしばしば別の言語で表されます。単に、ハッシュ関数の呼び出し回数を数えると言います。これは理にかなっています。通常はハッシュ関数のアプリケーションがハッシュテーブルアルゴリズムの実行時間の主要な項であるため、実際の時間の複雑さを分析するには、ハッシュ呼び出しの数を実行時間で乗算するだけです。ハッシュ関数の。

ハッシュテーブルをデータ構造として使用してアルゴリズムの実行時間を分析する場合、通常はRAMモデルの実際の実行時間に関心があることがよくあります。ここでの1つのオプションは、前の段落で提案したことを実行することです。つまり、ハッシュテーブル操作の実行時間(ハッシュ関数呼び出しの数で指定)にハッシュ関数の実行時間を掛けます。

ただし、キーの長さが異なる場合、これでは不十分です。たとえば、サイズがキーがあり、それぞれのハッシュを一度計算するとします。実際の時間の複雑さはですが、上記の計算ではしか得られません。これが一部のアプリケーションに当てはまる場合、基礎となるハッシュテーブルの複雑さの洗練された分析を使用して、アドホックベースでそれを考慮することができます。 O 2 MO M 2 M1,2,4,,2mO(2m)O(m2m)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.