首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Iter 2:适用于GMP大整数型的可重用、健壮的c++ std::hash<mpz_class>

Iter 2:适用于GMP大整数型的可重用、健壮的c++ std::hash<mpz_class>
EN

Code Review用户
提问于 2020-06-26 14:16:50
回答 2查看 141关注 0票数 6

这是代码评审的第二次迭代。第一次迭代(已完成)位于

Iter 1:可重用、健壮的c++ std::hash

1.目标

我的目的是提供一种快速哈希算法来散列GMP的S大整数类型mpz_classmpz_t,这样我就可以使用这些类型作为unordered_map的密钥。

2.当前方法

自C++17以来,标准库提供了专门化hash<string_view>,用于生成初始哈希值。

首先,将大整数的大小数据封装到string_view中,然后使用hash<string_view>计算其哈希值。这会产生一个初始的散列值,它只取决于大整数的大小,而不取决于符号。

为了保持正整数和负大整数的散列不同,只对负大整数置乱一次初始哈希值。

3.代码

文件hash_mpz.h

代码语言:javascript
复制
#ifndef HASH_MPZ_H_
#define HASH_MPZ_H_

#include <gmpxx.h>

namespace std {

template<> struct hash<mpz_srcptr> {
    size_t operator()(const mpz_srcptr x) const;
};

template<> struct hash<mpz_t> {
    size_t operator()(const mpz_t x) const;
};

template<> struct hash<mpz_class> {
    size_t operator()(const mpz_class &x) const;
};

}

#endif /* HASH_MPZ_H_ */

文件hash_mpz.cpp

代码语言:javascript
复制
#include "hash_mpz.h"
#include <cstddef>
#include <string_view>

constexpr size_t pi_size_t() {
    if (sizeof(size_t) == 4) {
        return 0xc90fdaa2; // floor(pi/4 * 2^32)
    } else if (sizeof(size_t) == 8) {
        return 0xc90fdaa22168c234; // floor(pi/4 * 2^64)
    } else {
        throw std::logic_error(
                "sizeof(size_t) not supported. only 32 or 64 bits are supported. you can easily add the required code for other sizes.");
    }
}

inline size_t scramble(size_t v) {
    return v ^ (pi_size_t() + (v << 6) + (v >> 2));
}

namespace std {

size_t std::hash<mpz_srcptr>::operator()(const mpz_srcptr x) const {
    string_view view { reinterpret_cast<char*>(x->_mp_d), abs(x->_mp_size)
            * sizeof(mp_limb_t) };
    size_t result = hash<string_view> { }(view);

    // produce different hashes for negative x
    if (x->_mp_size < 0) {
        result = scramble(result);
    }

    return result;
}

size_t hash<mpz_t>::operator()(const mpz_t x) const {
    return hash<mpz_srcptr> { }(static_cast<mpz_srcptr>(x));
}

size_t hash<mpz_class>::operator()(const mpz_class &x) const {
    return hash<mpz_srcptr> { }(x.get_mpz_t());
}

}

文件main.cpp

代码语言:javascript
复制
#include <iostream>
#include <gmpxx.h>
#include <unordered_map>

#include "hash_mpz.h"

using namespace std;

int main() {
    mpz_class a;

    mpz_ui_pow_ui(a.get_mpz_t(), 168, 16);

    cout << "a      : " << a << endl;
    cout << "hash( a): " << (hash<mpz_class> { }(a)) << endl;
    cout << "hash(-a): " << (hash<mpz_class> { }(-a)) << endl;

    unordered_map<mpz_class, int> map;
    map[a] = 2;
    cout << "map[a] : " << map[a] << endl;

    return 0;
}

4.问题

是否有任何可从进一步改善中获益的地方?

EN

回答 2

Code Review用户

回答已采纳

发布于 2020-06-29 18:38:22

使不应导出static

的函数

应该只在本地可用的函数应该标记为static。这适用于pi_size_t()scramble()中的hash_mpz.cpp

避免使用std::endl

使用"\n"而不是std::endl,后者相当于"\n",还强制输出同花顺。这很少是必要的,而且可能会影响性能,特别是在写入文件或标准输出重定向到文件时。

不考虑using namespace std

在头文件中您不是using namespace std,这是非常好的。但是请考虑根本不使用它,因为即使只在.cpp文件中使用,也会导致难以调试的命名空间冲突。如果您确实发现自己经常键入std::,并且希望避免输入,请考虑只从std::导入您确实使用的名称,如下所示:

代码语言:javascript
复制
using std::cout;
using std::unordered_map;

最终挑剔

  • std::中的namespace std块中还有一个不必要的hash_mpz.cpp
  • 您不需要在return 0的末尾使用main()
票数 3
EN

Code Review用户

发布于 2020-06-29 19:53:18

,你不是我的图书馆!

我认为向std添加更多的符号是不明智的。使用命名空间来定义库是很棒的,但是第三方用户需要使用std来获取不是STL代码的代码,这既令人惊讶,也令人困惑。你自己叫就行了。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/244571

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档