web-dev-qa-db-ja.com

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

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

128
AraK

セットのアイテムを繰り返し処理する場合、順序が重要です。

205
moonshadow

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

  • setは、unordered_setよりも lessメモリを使用して、同じ数の要素を格納します。
  • 少数の要素の場合、setのルックアップはunordered_setのルックアップよりも高速になる可能性があります。
  • 多くの操作はunordered_set平均ケースで高速ですが、多くの場合、の最悪ケースの方が保証されていますsetの複雑度(たとえば、insert)。
  • setは要素をソートします順番にアクセスしたい場合に便利です。
  • 異なるsetsを<<=>および>=辞書的に比較できます。これらの操作をサポートするためにunordered_setsは必要ありません。
294
sth

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

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

26
Mehrdad Afshari

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

6
ldog

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

6
anon

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

  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(この順序は、ハッシュ関数の影響を受ける可能性があります)

主に違い:

enter image description here

注:(場合によっては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 

setvector<int>をオーバーライドするため、operator<vectorのキーとして使用できる理由。

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

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
Jayhello

申し訳ありませんが、並べ替えられたプロパティについてもう1つ注目する価値があります。

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

nordered_setの場合、不可能です。

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

3
Mr. Pei

他の人々がすでに言及したことに加えて、もう一つ。要素をunordered_setに挿入するために予想される償却された複雑さはO(1)ですが、時々will take O(n)再構成する必要があります(バケットの数を変更する必要があります)-「良い」ハッシュ関数を使用する場合でも、ベクトルに要素を挿入するのと同じようにO(n)基礎となる配列を再割り当てする必要があります。

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

3
Blargle

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

この支配的なLinux C++実装のベンチマークを行い、違いを確認しました。

enter image description here

完全なベンチマークの詳細と分析は次の場所にあります: C++のSTLセットの基礎となるデータ構造は何ですか? ここでは繰り返しません。

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

簡単な要約として:

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

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

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

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

それとは別に、別の形式に変換したい場合は、関係を持たせるのが便利だと思います。

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

1
Rushyo

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

1
leiz