unordered_map哈希函数返回

时间:2014-07-17 21:44:10

标签: c++ struct set unordered-map hash-function

我希望unordered_map有一个struct,我希望将其用作多个std::set< std::string >的密钥。

我看到自定义哈希函数is required并且字符串可以应用std::hash;但是,我无法确定应该返回什么来满足unordered_map的这些集合的哈希函数的目的。

自定义散列函数应如何返回?

2 个答案:

答案 0 :(得分:2)

我认为这可能是Snps' answer的更好选择。这为用户定义的类型实现了std::hash的特化,并且它在不创建临时字符串的情况下散列结构。

我复制了Boost中的两个函数hash_combinehash_range,以计算两个容器中的单个哈希值。

#include <iostream>
#include <functional>
#include <set>
#include <unordered_map>

// user-defined type
struct myStruct {
    std::set<std::string> s1;
    std::set<std::string> s2;

    bool operator==(const myStruct &other) const {
        return (s1 == other.s1) && (s2 == other.s2);
    }
};

// hash helper functions plagiarized from Boost
template <typename T>
void hash_combine(size_t &seed, const T &v)
{
    using std::hash;
    seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

template <typename It>
void hash_range(size_t &seed, It first, It last)
{
    for (; first != last; ++first) {
        hash_combine(seed, *first);
    }
}

// std::hash specialization
namespace std
{
    template<> struct hash<myStruct> {
        size_t operator()(const myStruct &key) const {
            size_t seed = 0;
            hash_range(seed, key.s1.begin(), key.s1.end());
            hash_range(seed, key.s2.begin(), key.s2.end());
            return seed;
        }
    };
}

int main()
{
    std::unordered_map<myStruct, int> myMap;

    myStruct ms1{ { "apple", "pear", "orange" }, { "red", "green", "blue" } };
    myStruct ms2{ { "pear", "apple", "orange" }, { "red", "green", "blue" } };
    myStruct ms3{ { "apple", "banana", "orange" }, { "red", "green", "blue" } };

    myMap[ms1] = 1;
    myMap[ms2] = 2;
    myMap[ms3] = 3;

    std::cout << myMap.size() << '\n'; // output: 2
}

答案 1 :(得分:1)

std::hash的要求如下:(http://en.cppreference.com/w/cpp/utility/hash

哈希模板定义了一个实现哈希函数的函数对象。此函数对象的实例满足Hash。特别是,他们定义了一个 operator()

  1. 接受Key类型的单个参数。
  2. 返回类型size_t的值,表示参数的哈希值。
  3. 调用时不会抛出异常。
  4. 对于两个相等的参数k1k2 std::hash<Key>()(k1) == std::hash<Key>()(k2)
  5. 对于不相等的两个不同参数k1k2std::hash<Key>()(k1) == std::hash<Key>()(k2)应该非常小,接近1.0 / std::numeric_limits<size_t>::max()的概率。
  6. 哈希模板是 CopyConstructible 可破坏

    所以你需要的基本上是一个返回std::size_t的函数,它对每个myStruct对象都是唯一的,并且对于被认为是等价的对象返回相同的值。

    编辑:以下可能不是生成哈希的最有效方法,但它将作为如何完成哈希的基本示例。

    一种方法是使用std::hash<std::string>的标准专门化,方法是使用分隔符序列连接每个std::set成员中的所有字符串,然后连接所有结果将字符串合并为一个并使用标准哈希函数返回哈希值。

    如果成员myStruct不同,合并的&#34;超级&#34; -string对每个std::set对象都是唯一的,当成员不同时,它们仍然相同std::set是一个有序的容器。

    struct myStruct {
        std::set<std::string> s1;
        std::set<std::string> s2;
    };
    
    std::string mergeAllStrings(const myStruct& ms) {
        static const std::string SEPARATOR = "#¤%&"; // Some uncommon sequence.
        std::string super;
        for (const auto& s : ms.s1) {
            super += s + SEPARATOR; // Append separator.
        }
        for (const auto& s : ms.s2) {
            super += s + SEPARATOR; // Append separator.
        }
        return super;
    }
    
    int main() {
        myStruct ms1{{"apple", "pear", "orange"}, {"red", "green", "blue"}};
        myStruct ms2{{"pear", "apple", "orange"}, {"red", "green", "blue"}};
        myStruct ms3{{"apple", "banana", "orange"}, {"red", "green", "blue"}};
    
        std::cout << std::hash<std::string>()(mergeAllStrings(ms1)) << std::endl;
        std::cout << std::hash<std::string>()(mergeAllStrings(ms2)) << std::endl;
        std::cout << std::hash<std::string>()(mergeAllStrings(ms3)) << std::endl;
    }
    

    输出:

    2681724430859750844 // Same
    2681724430859750844 // Same
    2942368903851914580 // Different
    

    您现在可以创建一个哈希函子,例如为:

    struct MyHash {
        std::size_t operator()(const myStruct& ms) const {
            return std::hash<std::string>()(mergeAllStrings(ms));
        }
    };
    

    并将其与std::unordered_map一起用作:

    std::unordered_map<myStruct, myValue, MyHash> m;
    

    请注意,您还应该提供自定义equal_to仿函数。