[解決済み] std::unordered_mapの実装方法
疑問点
c++ unordered_map 衝突処理、リサイズとリハッシュ
これは私が以前行った質問ですが、unordered_mapの実装方法について多くの混乱があることがわかりました。他の多くの人々が私と同じように混乱することを確信しています。標準を読まなくても、私が知っている情報に基づいています。
すべてのunordered_mapの実装は、バケットの配列に外部ノードへのリンクリストを格納します。 ノードへのリンクリストをバケットの配列に格納する... いいえ、それは最も一般的な用途のためのハッシュマップを実装する最も効率的な方法では全くありません。 それは、ほとんどの一般的な用途において、ハッシュマップを実装する最も効率的な方法ではありません。 残念ながら、unordered_mapの仕様には小さな見落とし"があります。 の仕様に小さな見落としがあり、この振る舞いを要求しているのです。要求される動作は 要素へのイテレータは、他の要素を挿入または削除する際にも有効でなければならないことです。 他の要素
私は誰かが実装を説明し、それがどのようにC++標準の定義に適合するか(パフォーマンス要件の観点から)、そしてそれが本当にハッシュマップデータ構造を実装する最も効率的な方法ではない場合、どのように改善することができるか、期待していたのですが?
どのように解決するのですか?
この規格では、事実上
std::unordered_set
と
std::unordered_map
- とその同胞であるquot;multi"を使用します。
オープン ハッシュ
別名
セパレート チェーン
というのは、バケットの配列で、それぞれがリンクリストの先頭を保持していることを意味します†。 その要件は微妙です:それは結果です。
-
デフォルトの
max_load_factor()
は 1.0 です (つまり、テーブルがsize()
が1.0倍を超えるとbucket_count()
となります。 - は、その負荷率を超えて成長しない限り、テーブルがリハッシュされないことを保証します。
それは、ハッシュテーブル実装の他の主要なカテゴリとの衝突があるため、チェーニングなしでは実用的ではありません - 。
クローズド ハッシュ
別名
オープンアドレッシング
- が圧倒的な存在感を示すようになります。
load_factor()
](
https://en.cppreference.com/w/cpp/container/unordered_map/load_factor
) は1に近づく。
参考文献です。
<ブロッククオート
23.2.5/15:この
insert
と
emplace
の場合,メンバは,イテレータの有効性に影響を与えないものとする。
(N+n) < z * B
である場合,ここで
N
は挿入操作の前のコンテナ内の要素数である。
n
は挿入された要素の数である。
B
はコンテナのバケット数、そして
z
はコンテナの最大負荷率である。
のうち
効果
のうち、23.5.4.2/1にあるコンストラクタの
max_load_factor()
は以下を返します。
1.0
.
空のバケットを渡すことなく最適な反復処理を行うために、GCC の実装では、イテレータを持つバケットを、すべての値を保持する単一のシングルリンクリストに埋めます:イテレータはすぐに要素を指す
の直前の要素を指します。
イテレータはそのバケツの要素の直前の要素を指すので
next
のポインタは、バケツの最後の値を消去する際に、再配線することができます。
引用された文章について。
いいえ、それはほとんどの一般的な用途のためのハッシュマップを実装する最も効率的な方法では全くありません。残念ながら、unordered_map の仕様における小さな見落としが、この動作を要求しているのです。要求される動作は、他の要素を挿入または削除するときに、要素へのイテレータが有効であり続けることです。
見落としはありません...行われたことは非常に意図的であり、十分な認識を持って行われました。 他の妥協点があったことは事実ですが、オープン ハッシュ/チェーン方式は一般的な使用における妥当な妥協点であり、平凡なハッシュ関数による衝突に合理的に対処でき、キー/値の型の大小にかかわらず無駄がなく、任意の数の
insert
/
erase
のペアを、多くの閉じたハッシュの実装のように、徐々にパフォーマンスを低下させることなく使用できます。
意識の証拠に Matthew Austernの提案はこちら :
<ブロッククオート私は、汎用的なフレームワークでオープンアドレッシングを満足に実装したものを知りません。オープンアドレッシングは多くの問題を提示します。
- 空いている位置と占められた位置を区別することが必要です。
- ハッシュテーブルをデフォルトコンストラクタを持つ型に限定し、すべての配列要素を前もって構築しておくか、あるいはその要素のいくつかがオブジェクトで他のいくつかが生のメモリである配列を維持することが必要である。
- オープンアドレッシングは衝突管理を難しくします。ハッシュコードがすでに占有されている場所にマップされる要素を挿入する場合、次に試す場所を指示するポリシーが必要です。これは解決済みの問題ですが、最もよく知られている解決策は複雑です。
- 要素の消去が許可されている場合、衝突管理は特に複雑です。(議論のために Knuth を参照してください。) 標準ライブラリのコンテナ クラスは、消去を許可する必要があります。
- オープンアドレッシングのための衝突管理スキームは、最大N個の要素を保持できる固定サイズの配列を仮定する傾向があります。標準ライブラリのコンテナクラスは、利用可能なメモリの限界まで、新しい要素が挿入されたときに必要に応じて大きくすることができるはずです。
これらの問題を解決することは興味深い研究プロジェクトかもしれませんが、C++のコンテキストでの実装経験がない場合、オープンアドレスのコンテナクラスを標準化することは不適切でしょう。
具体的には、バケットに直接格納できるほど小さなデータ、未使用のバケット用の便利なセンチネル値、および優れたハッシュ関数を持つ挿入専用のテーブルの場合、閉じたハッシュアプローチはおよそ1桁速く、劇的に少ないメモリを使用するかもしれませんが、これは汎用的ではありません。
ハッシュ テーブルの設計オプションとその意味の完全な比較と詳細な説明は、ここで適切に扱うには広すぎるため、S.O. のトピックからは外れています。
関連
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み] 文字列の単語を反復処理するにはどうすればよいですか?
-
[解決済み] using namespace std;」はなぜバッドプラクティスだと言われるのですか?
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] なぜテンプレートはヘッダーファイルでしか実装できないのですか?
-
[解決済み] 些細なキーの場合、unordered_mapよりもmapを使用する利点はありますか?
-
[解決済み] gcc std::unordered_map の実装は遅いですか?もしそうなら、それはなぜですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] テスト
-
[解決済み】コンストラクターでのエラー:識別子を期待されますか?
-
[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み] to_string は std のメンバーではない、と g++ が言っている (mingw)
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++
-
[解決済み】'std::cout'への未定義の参照
-
[解決済み] スタックアロケーションにより初期化されていない値が作成された