Хешируйте произвольное значение точности (boost::multiprecision::cpp_int)
Мне нужно получить хэш значения с произвольной точностью (из Boost.Multiprecision); Я использую cpp_int
бэкенд. На данный момент я придумал следующий код:
boost::multiprecision::cpp_int x0 = 1;
const auto seed = std::hash<std::string>{}(x0.str());
Мне не нужно, чтобы код был настолько быстрым, насколько это возможно, но я считаю очень неуклюжим хешировать строковое представление.
Итак, мой вопрос состоит из двух частей:
- Сохраняя произвольную точность, могу ли я хэшировать значение более эффективно?
- Может быть, я не должен настаивать на сохранении произвольной точности, и я должен преобразовать в
double
который я мог бы легко хэшировать (однако я все равно делал бы сравнение, необходимое для хеш-таблицы, используя произвольное значение точности)?
2 ответа
Вы можете (ab) использовать поддержку сериализации:
Поддержка сериализации поставляется в двух формах: Классы
number
,debug_adaptor
,logged_adaptor
а такжеrational_adaptor
иметь "сквозную" поддержку сериализации, которая требует, чтобы базовый бэкэнд был сериализуемым.Backends
cpp_int
,cpp_bin_float
,cpp_dec_float
а такжеfloat128
есть полная поддержка Boost.Serialization.
Итак, позвольте мне объединить что-то, что работает с неупорядоченными контейнерами boost и std:
template <typename Map>
void test(Map const& map) {
std::cout << "\n" << __PRETTY_FUNCTION__ << "\n";
for(auto& p : map)
std::cout << p.second << "\t" << p.first << "\n";
}
int main() {
using boost::multiprecision::cpp_int;
test(std::unordered_map<cpp_int, std::string> {
{ cpp_int(1) << 111, "one" },
{ cpp_int(2) << 222, "two" },
{ cpp_int(3) << 333, "three" },
});
test(boost::unordered_map<cpp_int, std::string> {
{ cpp_int(1) << 111, "one" },
{ cpp_int(2) << 222, "two" },
{ cpp_int(3) << 333, "three" },
});
}
Давайте отправим соответствующие hash<>
реализации наших собственных hash_impl
специализация, которая использует Multiprecision и Serialization:
namespace std {
template <typename backend>
struct hash<boost::multiprecision::number<backend> >
: mp_hashing::hash_impl<boost::multiprecision::number<backend> >
{};
}
namespace boost {
template <typename backend>
struct hash<multiprecision::number<backend> >
: mp_hashing::hash_impl<multiprecision::number<backend> >
{};
}
Теперь, конечно, напрашивается вопрос, как hash_impl
реализованы?
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;
}
};
Это выглядит довольно просто. Это потому, что Boost потрясающий, и написание hash_sink
Устройство для использования с Boost Iostreams - это просто следующее простое упражнение:
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;
};
Полная демонстрация:
#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> >
{};
}
namespace boost {
template <typename backend>
struct hash<multiprecision::number<backend> >
: mp_hashing::hash_impl<multiprecision::number<backend> >
{};
}
template <typename Map>
void test(Map const& map) {
std::cout << "\n" << __PRETTY_FUNCTION__ << "\n";
for(auto& p : map)
std::cout << p.second << "\t" << p.first << "\n";
}
int main() {
using boost::multiprecision::cpp_int;
test(std::unordered_map<cpp_int, std::string> {
{ cpp_int(1) << 111, "one" },
{ cpp_int(2) << 222, "two" },
{ cpp_int(3) << 333, "three" },
});
test(boost::unordered_map<cpp_int, std::string> {
{ cpp_int(1) << 111, "one" },
{ cpp_int(2) << 222, "two" },
{ cpp_int(3) << 333, "three" },
});
}
Печать
void test(const Map&) [with Map = std::unordered_map<boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<> >, std::basic_string<char> >]
one 2596148429267413814265248164610048
three 52494017394792286184940053450822912768476066341437098474218494553838871980785022157364316248553291776
two 13479973333575319897333507543509815336818572211270286240551805124608
void test(const Map&) [with Map = boost::unordered::unordered_map<boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<> >, std::basic_string<char> >]
three 52494017394792286184940053450822912768476066341437098474218494553838871980785022157364316248553291776
two 13479973333575319897333507543509815336818572211270286240551805124608
one 2596148429267413814265248164610048
Как видите, разница в реализации между Boost и стандартной библиотекой unordered_map
показать в разных порядках для одинаковых хэшей.
Просто чтобы сказать, что я только что добавил встроенную поддержку хеширования (для Boost.Hash и std::hash) для разработки git. Он работает для всех типов чисел, в том числе из GMP и т. Д. К сожалению, этот код не будет выпущен до Boost-1.62.
Ответ выше, что (ab) использует поддержку сериализации, на самом деле очень крутой и действительно довольно умный;) Однако, это не сработает, если вы захотите использовать векторный хеш-код, такой как CityHash, я добавил пример использования этого путем доступа конечности непосредственно к документам: https://htmlpreview.github.io/?https://github.com/boostorg/multiprecision/blob/develop/doc/html/boost_multiprecision/tut/hash.html Либо прямой доступ к конечностям или совет сериализации будет работать со всеми предыдущими выпусками, конечно.