web-dev-qa-db-ja.com

セットの和集合を見つける最速の方法

set<pair<int,int> > x1, x2, ... xnのようなintのペアのセットがあります(nは2から20の間です)。それらの集合の和集合を見つける最も速い方法は何ですか?

申し訳ありませんが、最初に明確にしなかった場合は、パフォーマンスが速いことを意味しました。メモリ割り当ては問題ではありません。

13
Damir

残念ながら、すべての和集合は両方のセットの要素の組み合わせであるため、線形O(N)ソリューションに制限されていると思います。

template<typename S>
S union_sets(const S& s1, const S& s2)
{
     S result = s1;

     result.insert(s2.cbegin(), s2.cend());

     return result;
}
6

結果もセットである必要があると仮定すると、各_x_i_のすべての要素をその結果セットに挿入する以外に選択肢はありません。したがって、明らかな実装は次のとおりです。

_set<pair<int,int>> x(x1);
x.insert(x2.begin(), x2.end());
// etc
_

残りの問題は、これをスピードで打ち負かすことができるかどうかです。

単一要素のinsertpositionヒントを取ります。これは、正しい場合挿入を高速化します。したがって、mightこのようなものはx.insert(x2.begin(), x2.end());よりも高速であることがわかります。

_auto pos = x.begin()
for (auto it = x2.begin(); it != x2.end(); ++it) {
    pos = x.insert(pos, *it);
}
_

ただし、データによって異なります。その位置は正確な場合とそうでない場合があります。開始する前にすべての要素を順番に並べることで、確実に実行できます。最適なツールはおそらく_set_union_です。これは_merge_and_dedupe_sorted_ranges_という名前の方がよいかもしれません。これは、それが行うことは_std::set_とは特に関係がないためです。 _set_union_を中間ベクトルに変換するか、次のようなセットに変換することができます。

_set<pair<int,int>> x;
set_union(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end());
_

_set_union_の使用に関する私の懸念は、要素を昇順でセットに追加する利点を得るには、呼び出すたびに新しい空のコンテナーを作成する必要があることです(空でない場合は、要素が空であるため)すでに含まれている値とインターリーブする必要が追加されました)。これらのコンテナのオーバーヘッドは、任意の順序でセットに挿入するオーバーヘッドよりも高くなる可能性があります。テストする必要があります。

10
Steve Jessop

最初に最小のセットの和集合を見つけます。つまり、セットの長さでセットを並べ替え、2つの最小セットの和集合を計算し、それらのセットを削除し、そのサイズに従って和集合をセットリストに挿入します。

2つのセットがどの程度類似しているかを測定した場合は、最初に最も類似したセットの和集合を最初に見つけることが最善の策です。これは、重複を早期に排除するユニオン操作を優先します。

編集:そして、2つのセット間の和集合演算ごとに-小さいセットを大きいセットにマージします。

5
Rafael Baptista

fastとは、実装が速いという意味だと思います。

次に: std :: set_union (*)

2つのセットの例:

#include <set>
#include <algorithm>
#include <iterator>
using namespace std;

int main () {
    set<pair<int,int> > a, b, uni;
    set_union (a.begin(), a.end(),
               b.begin(), b.end(),
               inserter(uni, uni.begin()));

}

nセットの場合、手書きが最も保守しやすい解決策になる可能性があります。

#include <set>
#include <vector>
using namespace std;

int main () {
    vector<set<pair<int,int>>> sets;
    set<pair<int,int>> uni;

    for (const auto &s : sets)
        for (const auto &elem : s)
            uni.insert (elem);
}

ただし、一般的には、標準的なアルゴリズムを好み、その品質の実装から利益を得る必要があります。

fastがパフォーマンスを意味する場合、要件がないため、私たちは助けることができません。アプローチが異なれば、状況によって結果が異なる可能性があります。


(*)注:サイトは、標準に対して100%正確ではないために時々眉をひそめます

4
Sebastian Mach

ヘッダーアルゴリズムでset_unionを試してください。

3
Anon Mail

std :: set_union を再帰的に使用するか、単にすべてのセットを結果セットに挿入することができます(重複するアイテムはセットによって削除されます)。アイテムの数が非常に少ない場合は、すべてをベクターに挿入して並べ替え、ベクターで std :: unique を使用してみてください。

2
MadScientist

メモリ割り当てを節約し、局所性を向上させるには、単一の_vector<T>_を作業メモリとして使用することをお勧めします。

_vector<T>_を作成し、すべての要素の合計数を予約します(重複をカウントします)。次に、空の範囲[v.begin(), v.begin())から始めて、各セットの内容を追加し、マージして一意化することにより、セットのような(一意のソートされた)範囲に拡張します。

_vector<T> v;
v.reserve(<total size>);
for (set<T> &s: sets) {
    auto middle = v.insert(v.end(), s.begin(), s.end());
    inplace_merge(v.begin(), middle, v.end());
    v.erase(v.unique(v.begin(), v.end()), v.end());
}
_
2
ecatmur