这是代码评审的第二次迭代。第一次迭代(已完成)位于
我的目的是提供一种快速哈希算法来散列GMP的S大整数类型mpz_class和mpz_t,这样我就可以使用这些类型作为unordered_map的密钥。
自C++17以来,标准库提供了专门化hash<string_view>,用于生成初始哈希值。
首先,将大整数的大小数据封装到string_view中,然后使用hash<string_view>计算其哈希值。这会产生一个初始的散列值,它只取决于大整数的大小,而不取决于符号。
为了保持正整数和负大整数的散列不同,只对负大整数置乱一次初始哈希值。
文件hash_mpz.h:
#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:
#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:
#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;
}是否有任何可从进一步改善中获益的地方?
发布于 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::导入您确实使用的名称,如下所示:
using std::cout;
using std::unordered_map;std::中的namespace std块中还有一个不必要的hash_mpz.cpp。return 0的末尾使用main()。发布于 2020-06-29 19:53:18
我认为向std添加更多的符号是不明智的。使用命名空间来定义库是很棒的,但是第三方用户需要使用std来获取不是STL代码的代码,这既令人惊讶,也令人困惑。你自己叫就行了。
https://codereview.stackexchange.com/questions/244571
复制相似问题