web-dev-qa-db-ja.com

C ++で整数のペアの順序付けられていないセットを作成するにはどうすればよいですか?

次のプログラムは、整数のペアの順序付けられていないセットをコンパイルしませんが、整数に対してはコンパイルします。できる unordered_setとそのメンバー関数はユーザー定義型で使用されますが、どのように定義できますか?

#include <unordered_set>
...

class A{
...
private: 
    std::unordered_set< std::pair<int, int> > u_Edge_;
};

コンパイラエラー:

エラー: 'std :: unordered_set> :: unordered_set()'への呼び出しに一致する関数がありません

38
Pippi

コードはVS2010 SP1(VC10)でコンパイルされますが、GCC g ++ 4.7.2ではコンパイルできません。

ただし、boost::hash from Boost.Functional ハッシュするstd::pair(この追加により、コードはg ++でもコンパイルされます)。

#include <unordered_set>
#include <boost/functional/hash.hpp>

class A
{
private: 
    std::unordered_set< 
        std::pair<int, int>, 
        boost::hash< std::pair<int, int> > 
    > u_Edge_;
};
22
Mr.C64

ペアのハッシュを計算する標準的な方法はありません。この定義をファイルに追加します。

struct pair_hash {
    inline std::size_t operator()(const std::pair<int,int> & v) const {
        return v.first*31+v.second;
    }
};

これで、次のように使用できます。

std::unordered_set< std::pair<int, int>,  pair_hash> u_Edge_;

これは、pair<T1,T2>は平等を定義します。同等性をテストする方法を提供しないカスタムクラスの場合、2つのインスタンスが互いに等しいかどうかをテストするための個別の関数を提供する必要があります。

もちろん、このソリューションは2つの整数のペアに制限されます。 回答へのリンク は、複数のオブジェクトのハッシュを作成するより一般的な方法を定義するのに役立ちます。

36
dasblinkenlight

問題は、 std::unordered_setstd::hash テンプレートを使用してそのエントリのハッシュを計算し、ペアのstd::hash特殊化がないことです。したがって、次の2つのことを行う必要があります。

  1. 使用するハッシュ関数を決定します。
  2. その関数を使用して、std::hashをキータイプ(std::pair<int, int>)に特化します。

以下に簡単な例を示します。

#include <unordered_set>

namespace std {
template <> struct hash<std::pair<int, int>> {
    inline size_t operator()(const std::pair<int, int> &v) const {
        std::hash<int> int_hasher;
        return int_hasher(v.first) ^ int_hasher(v.second);
    }
};

}

int main()
{
    std::unordered_set< std::pair<int, int> > Edge;
}
8
user405725

std::hash<>で機能するstd::pair<int, int>に特化する必要があります。特殊化を定義する方法の非常に簡単な例を次に示します。

#include <utility>
#include <unordered_set>

namespace std
{
    template<>
    struct hash<std::pair<int, int>>
    {
        size_t operator () (std::pair<int, int> const& p)
        {
            // A bad example of computing the hash, 
            // rather replace with something more clever
            return (std::hash<int>()(p.first) + std::hash<int>()(p.second));
        }
    };
}

class A
{
private:
    // This won't give you problems anymore
    std::unordered_set< std::pair<int, int> > u_Edge_;
};
3
Andy Prowl

std::pair<int, int>>のハッシュ関数がありません。例えば、

struct bad_hash
{
  std::size_t operator()(const std::pair<int,int>& p) const
  {
    return 42;
  }
};

....

std::unordered_set< std::pair<int, int>, bad_hash> u_Edge_;

std::hash<T>std::hash<std::pair<int,int>>に特化することもできます。この場合、2番目のテンプレートパラメーターを省略できます。

1
juanchopanza

ここでの他の回答はすべて、何らかの方法で2つの整数を結合するハッシュ関数を作成することを示唆しています。

これは機能しますが、一意でないハッシュを生成します。これはunordered_setの使用には適していますが、一部のアプリケーションでは受け入れられない場合があります。あなたの場合、間違ったハッシュ関数を選択すると、多くの不必要な衝突が発生する可能性があります。

ただし、独自のハッシュを生成できます!

intは通常4バイトです。 int32_tを使用してこれを明示的にすることができます。

ハッシュのデータ型はstd::size_tです。ほとんどのマシンでは、これは8バイトです。これはコンパイル時に確認できます。

ペアは2つのint32_t型で構成されているため、両方の数値をstd::size_tに入れて一意のハッシュを作成できます。

これは次のようになります(ビット操作のために符号なしの値としてコンパイラに符号付きの値を処理させる方法を手っ取り早く思い出せないため、uint32_tについて次のように記述しました)。

#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <utility>


struct IntPairHash {
  std::size_t operator()(const std::pair<uint32_t, uint32_t> &p) const {
    assert(sizeof(std::size_t)>=8);  //Ensure that std::size_t, the type of the hash, is large enough
    //Shift first integer over to make room for the second integer. The two are
    //then packed side by side.
    return (((uint64_t)p.first)<<32) | ((uint64_t)p.second);
  }
};

int main(){
  std::unordered_set< std::pair<uint32_t, uint32_t>, IntPairHash> uset;
  uset.emplace(10,20);
  uset.emplace(20,30);
  uset.emplace(10,20);
  assert(uset.size()==2);
}
1
Richard

この質問に関する他のほとんどの回答ですでに述べたように、std::pair<int, int>のハッシュ関数を提供する必要があります。ただし、 C++ 11 なので、ハッシュ関数を定義する代わりに lambda expression を使用することもできます。次のコードは、ベースとして dasblinkenlightによって与えられた解決策 を取ります。

auto hash = [](const std::pair<int, int>& p){ return p.first * 31 + p.second; };
std::unordered_set<std::pair<int, int>, decltype(hash)> u_Edge_(8, hash);

イデオンのコード

Dasblinkenlightの免責事項を繰り返してください:このソリューションは2つの整数のペアに制限されています。 この回答 は、より一般的なソリューションのアイデアを提供します。

0
honk