web-dev-qa-db-ja.com

c ++-unordered_mapの複雑さ

(X、Y)ペアが特定のZ値に対応するルックアップ関数を作成する必要があります。これに対する1つの主要な要件は、可能な限りO(1)の複雑さでそれを行う必要があることです。私の計画は、unordered_mapを使用することです。

ルックアップ時間は私にとって重要ではなかったので、私は通常、ルックアップにハッシュテーブルを使用しません。衝突のないunordered_mapを作成している限り、ルックアップ時間はO(1)になると考えるのは正しいですか?

私の懸念は、キーが順序付けられていないマップに存在しない場合、複雑さがどうなるかということです。たとえば、unordered_map :: find():を使用して、キーがハッシュテーブルに存在するかどうかを判断した場合、どのように答えが返されますか?実際にすべてのキーを繰り返し処理しますか?

私は助けに大いに感謝します。

14
user1764386

標準では、衝突解決にバケットを使用する必要があります。つまり、実際のルックアップ時間は、要素が存在するかどうかに関係なく、バケット内の要素の数に対して線形になる可能性があります。 O(lg N)にすることは可能ですが、ハッシュテーブルが正しく使用されている場合、バケット内の要素の数shouldが少ないため、通常は実行されません。

バケット内の要素の数を少なくするには、ハッシュ関数が有効であることを確認する必要があります。効果的な意味は、ハッシュされるタイプと値によって異なります。 (MSの実装では、最も一般的なハッシュの1つであるFNVを使用しますが、実際に表示されるデータについて特別な知識がある場合は、より適切に実行できる可能性があります。)数を減らすのに役立つもう1つのことバケットあたりの要素の数は、より多くのバケットを強制するか、より小さな負荷係数を使用することです。 1つ目は、最小初期バケット数を引数としてコンストラクターに渡すことができます。マップに含まれる要素の総数がわかっている場合は、この方法で負荷率を制御できます。 rehashを呼び出すことにより、テーブルがいっぱいになったら、バケットの最小数を予測することもできます。それ以外の場合は、使用できる関数std::unordered_map<>::max_load_factorがあります。何かをすることは保証されていませんが、合理的な実装ではそうなります。すでに入力されているunordered_mapで使用する場合は、後でunordered_map<>::rehashを呼び出す必要があることに注意してください。

(標準のunordered_mapについて私が理解していないことがいくつかあります:負荷係数がfloatではなくdoubleである理由、効果を発揮する必要がない理由、効果がない理由自動的にrehashを呼び出さないでください。)

6
James Kanze

ハッシュされたデータ構造で衝突が発生しないようにすることは非常に困難です(特定のハッシュ関数やあらゆる種類のデータで不可能ではないにしても)。また、キーの数と正確に等しいテーブルサイズが必要になります。いいえ、それほど厳密である必要はありません。ハッシュ関数が比較的均一な方法で値を分散する限り、O(1)ルックアップの複雑さがあります。

ハッシュテーブルは通常、衝突を処理するリンクリストを備えた単なる配列です(これは連鎖方法です。他の方法もありますが、衝突を処理するために最も利用される方法である可能性があります)。したがって、値がバケット内に含まれているかどうかを確認するには、そのバケット内のすべての値を(潜在的に)反復する必要があります。したがって、ハッシュ関数が一様分布を提供し、Nバケットがあり、合計M値がある場合、バケットごとに(平均して)M/N値があるはずです。この値が大きすぎない限り、これによりO(1)ルックアップが可能になります。

したがって、あなたの質問に対する少し長い答えとして、ハッシュ関数が合理的である限り、O(1)ルックアップを取得し、(平均して)O(M/N)を反復処理する必要があります。あなたに「否定的な」結果を与えるための鍵。

3
Yuushi