用 cpp_int 构建一个大的 boost unordered_map

Posted

技术标签:

【中文标题】用 cpp_int 构建一个大的 boost unordered_map【英文标题】:Building a large boost unordered_map with cpp_int 【发布时间】:2018-02-08 09:32:36 【问题描述】:

我正在用 c++ 编写一些代码,用于需要使用多精度库(如 boost)的类分配。基本上,我需要用一些大整数构建一个哈希表,然后在该表中查找某个值。

当我使用已注释掉的 h、g、p 时 - 代码运行良好且速度非常快。一旦我切换到未注释掉的那些,它会在以下行引发内存异常: hash_str>::iterator got = mp.find(lkp); 我刚开始使用 c++ 并且很确定有些事情还很遥远,因为它应该运行得相当快,即使是大量的。

#include <boost/unordered_map.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/math/special_functions/pow.hpp>

using namespace std;
using namespace boost::multiprecision;

template <typename T>
struct hash_str

    size_t operator()( const T& t ) const
    
        return std::hash<std::string>()
            ( t.str() );
    
;

int main()

    boost::unordered_map<cpp_int, cpp_int, hash_str<cpp_int>> mp;
    //boost::unordered_map<hash_str<cpp_int>, cpp_int, hash_str<cpp_int>> mp;
    cpp_int k;
    cpp_int h( "3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333" );
    cpp_int g( "11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568" );
    //cpp_int g = 1010343267;
    //cpp_int h = 857348958;
    //cpp_int p = 1073676287;
    cpp_int p( "13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171" );
    int b = pow( 2, 20 );
    cpp_int denom;
    cpp_int inv = powm( g, p - 2, p );

    //building a hash table of all values h/g^x1
    for ( cpp_int x = 1; x < b; ++x )

    
        // go through all 2^20 values up to b, calculate the function h/g^x1, 
        // then hash it to put into table

        denom = powm( inv, x, p );
        k = ( h *denom ) % p;
        mp.insert( std::make_pair( k, x ) );


    
    cpp_int lkp;
    for ( int v = 1; v < b; ++v )
    
        //cpp_int gb = pow(g, b);
        lkp = powm( g, v*b, p );
        //looking for a match for g^b^x0 in map mp; when found we need to find x 
        //which is x1 and then calc 'x'
        boost::unordered::unordered_map<cpp_int, cpp_int, hash_str<cpp_int>>::iterator got = mp.find( lkp );
        // Check if iterator points to end of map or if we found our value
        if ( got != mp.end() )
        
            std::cout << "Element Found - ";
            //std::cout << got->first << "::" << got->second << std::endl;
        
        /*else
        
        std::cout << "Element Not Found" << std::endl;
        */
    
    return 0;


以防万一,这是我得到的异常: MiM.exe 中 0x768F2F71 处未处理的异常:Microsoft C++ 异常:boost::exception_detail::clone_impl > 在内存位置 0x0109EF5C。

【问题讨论】:

嗯,这些数字相当大,可能会耗尽 32 位进程的可用内存。尝试为 x64 平台构建。 也许我应该对这些数字进行十六进制和哈希处理,然后将它们放入 unordered_map。谁能为非常大的整数推荐一个快速散列? 【参考方案1】:

散列函数非常糟糕,因为它分配一个临时字符串只是为了散列它。该字符串的长度为 log(bits)/log(10) 个字节。

哈希的意义在于它是一种比较数字的相对快速的方法。使用如此昂贵的哈希,您最好使用常规 Tree 容器(例如 std::map)。

我没有检查过你的公式(尤其是在h/g^x1 附近,因为我什至不确定x 是否代表x1)。除了这个问题, 我认为至少如果您使用 32 位整数编译器,v * b 会溢出 int 容量存在正确性问题。

我已经清理了一点,它运行了

#include <boost/math/special_functions/pow.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/unordered_map.hpp>
#include <chrono>

namespace bmp = boost::multiprecision;
using namespace std::chrono_literals;
using Clock = std::chrono::high_resolution_clock;

template <typename T> struct hash_str 
    size_t operator()(const T &t) const  return std::hash<std::string>()(t.str()); 
;

template <typename T> struct hash_bin 
    size_t operator()(const T &t) const 
        return boost::hash_range(t.backend().limbs(), t.backend().limbs()+t.backend().size());
    
;
int main() 
    using bmp::cpp_int;
    boost::unordered_map<cpp_int, cpp_int, hash_bin<cpp_int> > mp;
#if 1
    cpp_int const h("32394751040504504435652643787280657886490975209524495278347924529719819761432925580738569379585531805328"
            "78928001494706097394108577585732452307673444020333");
    cpp_int const g("11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928"
            "594829675428279466566527115212748467589894601965568");
    cpp_int const p("13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690"
            "031858186486050853753882811946569946433649006084171");
#else
    cpp_int const g = 1010343267;
    cpp_int const h = 857348958;
    cpp_int const p = 1073676287;
#endif
    int constexpr b   = 1 << 20;
    cpp_int const inv = powm(g, p - 2, p);

    
        auto s = Clock::now();

        // building a hash table of all values h/g^x1
        for (cpp_int x = 1; x < b; ++x) 
            // go through [1, b), calculate the function h/g^x1,
            // then hash it to put into table

            cpp_int denom = powm(inv, x, p);
            cpp_int k = (h * denom) % p;
            mp.emplace(std::move(k), x);
        

        std::cout << "Built map in " << (Clock::now() - s)/1.0s << "s\n";
    

    
        auto s = Clock::now();

        for (cpp_int v = 1; v < b; ++v) 
            //std::cout << "v=" << v << " b=" << b << "\n";
            // cpp_int gb = pow(g, b);
            cpp_int const lkp = powm(g, v * b, p);

            // looking for a match for g^b^x0 in map mp; when found we need to find x
            // which is x1 and then calc 'x'
            auto got = mp.find(lkp);

            // Check if iterator points to end of map or if we found our value
            if (got != mp.end()) 
                std::cout << "Element Found - ";
                //std::cout << got->first << " :: " << got->second << "\n";
            
        
        std::cout << "Completed queries in " << (Clock::now() - s)/1.0s << "s\n";
    

对我来说,它在 1 分钟 4 秒内运行。

Built map in 24.3809s
Element Found - Completed queries in 39.2463s
...

使用hash_str 而不是hash_bin 需要1m13s:

Built map in 30.3923s
Element Found - Completed queries in 42.488s

【讨论】:

谢谢你,sehe,这非常有帮助。是的,x 是 x1,我只是更改了术语并忘记更新评论。但由于某种原因,使用您的代码我遇到了同样的事情,只是需要永远运行。事实上,未清理的版本至少会到第二个循环,这个只是位于第一个循环中。我想知道我的编译器是否还有其他问题(我使用的是 Visual Studio 2017 免费版)。 将 b 减少到 2^16 它在 MSVC (Version 19.00.23506 for x64) online: Built map in 6.05741s Completed queries in 9.75423s 上运行(这是大因素)。您是否启用了“发布模式”(即优化?) 原谅我的无知,但是当我进入配置并设置为发布时,Visual Studio 给了我 27 个错误(无法打开源文件等)。 哦,那是另一个问题。但现在是你最大的障碍。在 SO/MSDN 中搜索“构建配置”和“属性表”。这不是我的专业领域,因为我不喜欢使用 Visual Studio。 再次感谢您!通过打开优化,我能够在调试模式下运行它。当然,是没有优化的调试模式杀死了内存。真的很感激。

以上是关于用 cpp_int 构建一个大的 boost unordered_map的主要内容,如果未能解决你的问题,请参考以下文章

您如何使用任何 Boost 多精度库类型找到两个非常大的数的模逆? (cpp_int、gmp_int 等)

具有两个 cpp_int 值的 boost::multiprecision::pow

PyBind11:boost::multiprecision::cpp_int 到 Python

为啥在这里使用 boost::multiprecision::cpp_int 会影响尾调用优化

提升多精度 cpp_int 乘以浮点数

boost-multiprecision cpp_int 的最高限制是多少?