【问题标题】:C++: Using pair of (cpp_int, int) integers as key in unordered_map (where cpp_int are boost multiprecision integers)C++:使用一对 (cpp_int, int) 整数作为 unordered_map 中的键(其中 cpp_int 是提升多精度整数)
【发布时间】:2018-05-23 17:26:44
【问题描述】:

对于我的无序映射,我想将(cpp_int, int) 用作键对,其中cpp_intboost multiprecision 整数:

#include <boost/multiprecision/cpp_int.hpp>
#include <unordered_map>

using boost::multiprecision::cpp_int;

std::unordered_map<std::pair<cpp_int, int>, double> myMap

在这个网站上搜索我发现了许多使用 std::pair&lt;int,int&gt; 的自定义哈希函数作为键的建议,但我找不到如何处理 std::pair&lt;cpp_int, int&gt;

更新:为了澄清,我尝试了我在网上找到的哈希函数(for (int,int):

#include <boost/multiprecision/cpp_int.hpp>
#include <unordered_map>
using boost::multiprecision::cpp_int;
typedef std::pair<cpp_int, int> MyPair;

struct MyHash {
public:
        size_t operator()(MyPair x) const throw() {
             size_t h = x.first * 1 + x.second * 100000;
             return h;
        }
};

void function()
{
    std::unordered_map<MyPair, double, MyHash> M;
}

这不能编译:

error: cannot convert ‘boost::enable_if_c<true, boost::multiprecision::detail::expression<boost::multiprecision::detail::multiply_add, boost::multiprecision::detail::expression<boost::multiprecision::detail::terminal, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<> >, void, void, void>, boost::multiprecision::detail::expression<boost::multiprecision::detail::terminal, int, void, void, void>, int, void> >::type {aka boost::multiprecision::detail::expression<boost::multiprecision::detail::multiply_add, boost::multiprecision::detail::expression<boost::multiprecision::detail::terminal, boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<> >, void, void, void>, boost::multiprecision::detail::expression<boost::multiprecision::detail::terminal, int, void, void, void>, int, void>}’ to ‘size_t {aka long unsigned int}’ in initialization
              size_t h = x.first * 1 + x.second * 100000;
                                                  ^

我的问题是:如何在 unordered_map 中使用成对的 (cpp_int,int) 作为键?

非常感谢您!

更新 2: 感谢@sehe 将我指向his answer(他在其中为cpp_int 提供了一个哈希函数)。结合this answer(它展示了如何将两个哈希函数组合成一对),我想出了以下解决方案(它编译得很好,我需要测试我的问题,看看它是否有效):

#include <boost/archive/binary_oarchive.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_int/serialize.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/iostreams/stream.hpp>

#include <boost/functional/hash.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <unordered_map>


using boost::multiprecision::cpp_int;

typedef std::pair<cpp_int, int> MyPair;



namespace mp_hashing {
    namespace io = boost::iostreams;

    struct hash_sink {
        hash_sink(size_t& seed_ref) : _ptr(&seed_ref) {}

        typedef char         char_type;
        typedef io::sink_tag category;

        std::streamsize write(const char* s, std::streamsize n) {
            boost::hash_combine(*_ptr, boost::hash_range(s, s+n));
            return n;
        }
      private:
        size_t* _ptr;
    };

    template <typename T> struct hash_impl {
        size_t operator()(T const& v) const {
            using namespace boost;
            size_t seed = 0;
            {
                iostreams::stream<hash_sink> os(seed);
                archive::binary_oarchive oa(os, archive::no_header | archive::no_codecvt);
                oa << v;
            }
            return seed;
        }
    };
}

namespace std {
    template <typename backend>
    struct hash<boost::multiprecision::number<backend> >
        : mp_hashing::hash_impl<boost::multiprecision::number<backend> >
    {};
}


struct pair_hash {
    template <class T1, class T2>
    std::size_t operator () (const std::pair<T1,T2> &p) const {
        auto h1 = std::hash<T1>{}(p.first);
        auto h2 = std::hash<T2>{}(p.second);

        // Mainly for demonstration purposes, i.e. works but is overly simple
        // In the real world, use sth. like boost.hash_combine
        return h1 ^ h2;
    }
};

void function()
    {
        std::unordered_map<MyPair, double, pair_hash> M;
    }

【问题讨论】:

  • 你有 cpp_int 的哈希吗?你知道如何组合哈希吗?如果两者都是,你的问题是什么?如果没有,您尝试过什么?
  • @Ron 我已经更新了问题。抱歉,不是很清楚。
  • @Yakk 我对这些东西很陌生,我还没有在网上找到我需要的东西,所以...我没有 cpp_int 的哈希值。
  • 你现在可以了,@Khue(下一次,只需搜索 :))。这是一个使用 unordered_map&lt;cpp_int, int&gt; 的 MSVC 现场演示:rextester.com/l/cpp_online_compiler_visual /cc @Yakk
  • 是的,正如我所写:“主要用于演示目的,即工作但过于简单”因为无论如何您都在使用 boost。 boost.hash_combine 或者,更直接地说,boost::hash_value(std::pair&lt;T,U&gt;) 是要走的路。 boost.org/doc/libs/1_65_1/doc/html/hash/reference.html

标签: c++ dictionary boost


【解决方案1】:

是的,您使用了我之前提供的多精度哈希并添加了 std::pair 的哈希。我不喜欢手动处理哈希组合(良好的通用哈希组合并非易事)。

所以我会对boost::hash_combine 做同样的事情:

template <typename K, typename V>
struct hash<std::pair<K, V> > 
{
    size_t operator()(std::pair<K, V> const& pair) const {
        size_t seed = std::hash<K>{}(pair.first);
        boost::hash_combine(seed, pair.second);
        return seed;
    }
};

Live On MSVC RexTester

#include <iostream>
#include <iomanip>

#include <boost/archive/binary_oarchive.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_int/serialize.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/iostreams/stream.hpp>

#include <boost/functional/hash.hpp>

namespace mp_hashing {
    namespace io = boost::iostreams;

    struct hash_sink {
        hash_sink(size_t& seed_ref) : _ptr(&seed_ref) {}

        typedef char         char_type;
        typedef io::sink_tag category;

        std::streamsize write(const char* s, std::streamsize n) {
            boost::hash_combine(*_ptr, boost::hash_range(s, s+n));
            return n;
        }
      private:
        size_t* _ptr;
    };

    template <typename T> struct hash_impl {
        size_t operator()(T const& v) const {
            using namespace boost;
            size_t seed = 0;
            {
                iostreams::stream<hash_sink> os(seed);
                archive::binary_oarchive oa(os, archive::no_header | archive::no_codecvt);
                oa << v;
            }
            return seed;
        }
    };
}

#include <unordered_map>
#include <boost/unordered_map.hpp>

namespace std {
    template <typename backend> 
    struct hash<boost::multiprecision::number<backend> > 
        : mp_hashing::hash_impl<boost::multiprecision::number<backend> > 
    {};

    template <typename K, typename V>
    struct hash<std::pair<K, V> > 
    {
        size_t operator()(std::pair<K, V> const& pair) const {
            size_t seed = std::hash<K>{}(pair.first);
            boost::hash_combine(seed, pair.second);
            return seed;
        }
    };
}

int main() {
    using boost::multiprecision::cpp_int;

    std::unordered_map<std::pair<cpp_int, int>, int>  m {
        { { cpp_int(1) << 111, -1 }, 1 },
        { { cpp_int(2) << 222, -2 }, 2 },
        { { cpp_int(3) << 333, -3 }, 3 },
    };

    for (auto& p : m)
        std::cout << p.first.first << " -> " << p.second << "\n";
}

【讨论】:

  • 请注意,boost::hash_value 已经为std::pair 提供了重载。
  • @BaummitAugen Cool - 我以为我记得这一点,但今天早些时候不知何故偶然发现了它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-17
  • 1970-01-01
  • 2017-09-11
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多