unordered_setの代わりにsetを使用するのはなぜですか?


145

C ++ 0xは、他の多くの場所でunordered_set利用可能なものを導入していますboost。私が理解しているのunordered_setは、O(1)ルックアップが複雑なハッシュテーブルです。一方、ルックアップが複雑なsetツリーにすぎませんlog(n)なぜ誰もがset代わりに使うのunordered_setでしょうか?すなわち、setもはや必要性はありますか?


22
あなたの質問は根本的に木がもう必要かどうかを尋ねています。
Vinko Vrsalovic 08

2
最初の行で、これは何となくばかげた質問だとはっきり言ったと思います。私は何かを逃していた、そして今私は答えを得た:)
AraK

2
本当の理由は、物事は見た目ほど白黒ではないということです。中間にはたくさんのグレーや他の色があります。これらのコンテナはツールであることを覚えておく必要があります。時々パフォーマンスは重要ではなく、利便性ははるかに意味があります。人々は、すべて私たちが「D最初の場所でC ++(Pythonのはもちろんのこと)を使用しないと、継続的に機械語で書くと最適化コード決して最も効率的な解決策を探した場合。
AturSams

(なぜ一体誰が実装/インターフェースに一般名を使用し、その名前が暗示するものを超えた約束をして、そうでないものにとって厄介な状況を作り出すのですか?)
greybeard

回答:


219

セットのアイテムを繰り返し処理したい人にとって、順序は重要です。


挿入順、または演算子を使用した実際の比較に従って順序付けされてい< >ますか?
SomethingSomething

2
デフォルトではstd :: lessを使用して注文されます。これをオーバーライドして、独自の比較演算子を指定できます。cplusplus.com/reference/set/set
moonshadow

または、順序が重要ではない場合でも、反復したい場合もあります。
mfnx

319

順序付けされていないセットは、いくつかの方法でO(1)平均アクセス時間を支払う必要があります。

  • set同じ数の要素を格納するよりも少ないメモリを使用しunordered_setます。
  • 以下のための要素の数が少ない、内検索がsetあるかもしれないより高速での検索よりもunordered_set
  • 多くの操作が高速であるにもかかわらず、平均的なケースのためにunordered_set、彼らはしばしば持つことが保証されている優れた最悪の複雑さをするためにset(例えばinsert)。
  • これは、set 要素がソートあなたが順序でそれらにアクセスする場合に便利です。
  • あなたはできる辞書順比較異なるsetとSを<<=>>=unordered_setこれらの操作をサポートするためには必要ありません。


9
+1、すべての優れた点。ハッシュテーブルには平均ケースのアクセス時間がO(1)であるという事実を見落とす傾向があります。つまり、大きな遅延が発生することがあります。区別は、リアルタイムシステムにとって重要です。
j_random_hacker

良い点ですが、ここ(en.cppreference.com/w/cpp/container/unordered_set/operator_cmp)では、unordered_setsを比較できると述べられています。
Michiel uit het Broek

5
「少数の要素」を定義する
Sunjay Varma

4
@SunjayVarmaは通常、100要素が2つの間の適切なカットオフです。疑わしい場合は、特定のユースケースで2つのテストのパフォーマンスを置き換えるものはありません。
ネイト

3
@MichieluithetBroek順序付けではなく、等価比較のみが記述されています(<)。
lisyarus 2017年

26

ハッシュテーブルよりもツリーを好むときはいつでも。

たとえば、ハッシュテーブルは最悪の場合 "O(n)"です。O(1)は平均的なケースです。木は最悪の場合「O(log n)」です。


18
/ Balanced /ツリーは、最悪の場合O(ln n)です。最終的にO(n)ツリー(本質的にリンクされたリスト)になる可能性があります。
ストレッジャー2009

5
合理的にインテリジェントなハッシュ関数を記述できる場合、ほとんどの場合、ハッシュテーブルからO(1)パフォーマンスを得ることができます。このようなハッシュ関数を作成できない場合、つまり、セットに対して「順番に」繰り返す必要がある場合は、ツリーを使用する必要があります。しかし、「O(n)最悪の場合のパフォーマンス」を恐れているので、ツリーを使用しないでください。
ジャスティンL.

6
ステージャー:熟考するために、はい。ただし、C ++でのセットについては、通常、バランスのとれたバイナリ検索ツリーとして実装されています。複雑さについて話すには、実際の操作を指定する必要があります。この文脈では、ルックアップについて話していることは明らかです。
Mehrdad Afshari、

1
Justin L:木を好む理由の1つにすぎません。私の答えの中核は最初の行です。たび、あなたはハッシュテーブルにツリーデータ構造を好みます。ハッシュテーブルよりもツリーの方が好ましいケースはたくさんあります。ハッシュテーブルは、特に「範囲の交差」のようなものを吸います。
Mehrdad Afshari、

2
stlツリーは、ほぼ普遍的に実装された赤黒ツリーであり、高度な自己平衡ツリーです。実際には、最悪の場合のO(n)ルックアップが受け入れられない場合があります。悪意のあるユーザーが特別に細工された値を保存することでDoSを効果的に作成する可能性があるため、ユーザーの値を保存するインターフェースを提供するWebサービスではハッシュマップを使用しないでください。重要な、時間に敏感なシステムでは、O(n)ルックアップ、航空管制なども許可されない場合があります。一般的には問題ありませんが、デフォルトでハッシュマップを使用し、本当に必要な場合にのみツリーバージョンを切り替えます。
deft_code 2009

14

次の場合にセットを使用:

  1. 順序付けされたデータ(個別の要素)が必要です。
  2. データを(ソート順に)印刷/アクセスする必要があります。
  3. 要素の前任者/後継者が必要です。

unordered_setは次の場合に使用します。

  1. 一連の別個の要素を保持する必要があり、順序付けは必要ありません。
  2. 単一要素へのアクセス、つまり全探索は必要ありません。

例:

セットする:

入力:1、8、2、5、3、9

出力:1、2、3、5、8、9

Unordered_set:

入力:1、8、2、5、3、9

出力:9 3 1 8 2 5(おそらくこの順序で、ハッシュ関数の影響を受けます)

主な違い:

ここに画像の説明を入力してください

注:(場合によってsetはより便利です)たとえばvectorキーとして使用

set<vector<int>> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});

for(const auto& vec:s)
    cout<<vec<<endl;   // I have override << for vector
// 1 2
// 1 3 

オーバーライドのためにvector<int>キーとして使用できる理由。setvectoroperator<

ただし、を使用するunordered_set<vector<int>>場合は、のハッシュ関数を作成する必要があります。これはvector<int>、vectorにはハッシュ関数がないため、次のように定義する必要があるためです。

struct VectorHash {
    size_t operator()(const std::vector<int>& v) const {
        std::hash<int> hasher;
        size_t seed = 0;
        for (int i : v) {
            seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }
        return seed;
    }
};

vector<vector<int>> two(){
    //unordered_set<vector<int>> s; // error vector<int> doesn't  have hash function
    unordered_set<vector<int>, VectorHash> s;
    s.insert({1, 2});
    s.insert({1, 3});
    s.insert({1, 2});

    for(const auto& vec:s)
        cout<<vec<<endl;
    // 1 2
    // 1 3
}

場合によってunordered_setはより複雑であることがわかります。

主に以下から引用:https : //www.geeksforgeeks.org/set-vs-unordered_set-c-stl/ https://stackoverflow.com/a/29855973/6329006


6

std :: setは標準C ++の一部であり、unordered_setはそうではないためです。C ++ 0xは標準ではなく、Boostも標準ではありません。私たちの多くにとって、移植性は不可欠であり、それは標準に固執することを意味します。


2
私が彼を正しく理解していれば、彼はなぜ人々が現在まだセットを使用しているのか尋ねていません。彼はC ++ 0xについて自分自身に知らせています。
ヨハネスシャウブ-litb

2
多分。ハッシュテーブルとツリーによってさまざまな問題が解決されることは誰もが知っていると思いました。

21
まあ、それは標準です(たった数年かかった)
クレイトンヒューズ

6

スイープラインアルゴリズムを検討してください。これらのアルゴリズムはハッシュテーブルでは完全に失敗しますが、バランスの取れたツリーでは美しく機能します。スイープラインアルゴリズムの具体例を示すために、フォーチュンのアルゴリズムを考えてみましょう。http://en.wikipedia.org/wiki/Fortune%27s_algorithm


1
質問を考えると、そのような参照は複雑すぎると思います。(私はそれを調べなければならなかった)
hectorpal 2015年

3

他の人々がすでに言及したことに加えて、もう一つ。unordered_setに要素を挿入するための償却複雑さはO(1)であり、すべての今して、それは期待しながらハッシュテーブルのニーズを再構築するため(変化するバケットの必要数)O(n)を取る-偶数と「良い」ハッシュ関数。基になる配列を再割り当てする必要があるため、ベクターに要素を挿入するとO(n)が時々かかります。

セットへの挿入には常に最大でO(log n)かかります。一部のアプリケーションでは、これが望ましい場合があります。


3

すみません、sortedプロパティについてもう1つ注意する必要があります。

たとえば、コンテナに一定範囲のデータが必要な場合:setに時間を保存し、2013-01-01から2014-01-01までの時間を必要とします。

unordered_setことは不可能です。

もちろん、この例はmapunordered_mapの間の使用例に対してより説得力があります。


3

g++ 6.4 stdlibc ++の順序付きと順序なしのセットのベンチマーク

この主要なLinux C ++実装をベンチマークして、違いを確認しました。

ここに画像の説明を入力してください

完全なベンチマークの詳細と分析は、C ++のSTLセットの基本的なデータ構造は何ですか?ここでは繰り返しません。

「BST」は「テスト済み」を意味しstd::set、「ハッシュマップ」は「テスト済み」を意味しstd::unordered_setます。「ヒープ」はstd::priority_queue私が分析したものです:ヒープとバイナリ検索ツリー(BST)

簡単な要約として:

  • グラフは、これらの条件下では、10万を超えるアイテムがある場合、ハッシュマップの挿入は常にはるかに高速であり、アイテムの数が増えるにつれて差が大きくなることを明確に示しています

    この速度向上のコストは、順番に効率的に移動できないことです。

  • 曲線は、ordered std::setがBSTベースでstd::unordered_setハッシュマップベースであることを明確に示唆しています。参考回答では、GDBステップによってコードをデバッグしていることをさらに確認しました。

mapvs に対する同様の質問unordered_map些細なキーの場合にunordered_mapよりもmapを使用する利点はありますか?


1

一方で、別の形式に変換したい場合は、関係にあると便利です。

また、アクセスは高速ですが、インデックスを作成する時間や、インデックスの作成やアクセスに使用されるメモリが長くなる可能性もあります。


+ 1、Big Oh表記は一定の要因を隠します。典型的な問題のサイズでは、多くの場合、最も重要なのは一定の要因です。
j_random_hacker 09/09/03

1

ソートしたい場合は、unordered_setの代わりにsetを使用します。unordered_setは、保存された順序が重要でない場合に、セットで使用されます。


1

この回答は10年遅れる可能性がありますstd::unordered_setが、セキュリティの欠点もあることを指摘しておく価値があります。

ハッシュ関数が予測可能である場合(これは、ランダム化されたソルトなどの対策を適用しない限り、通常これに該当します)、攻撃者はハッシュの衝突を生成し、すべての挿入とルックアップにO(n)時間を要するデータを手作業で作成できます。

これは非常に効率的で洗練されたサービス拒否攻撃に使用できます。

内部でハッシュマップを使用する言語の多くの(ほとんど?)実装がこれに遭遇しました:

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