C++ 提供一个好的散列函数
在我的初始问题(详细的实验调查)中:我使用未排序集管理随机N维浮点数组,并使用我的初始(可能设计糟糕的哈希函数),得到了非常奇怪的行为:C++ 提供一个好的散列函数,c++,stl,performance-testing,unordered-set,hash-function,C++,Stl,Performance Testing,Unordered Set,Hash Function,在我的初始问题(详细的实验调查)中:我使用未排序集管理随机N维浮点数组,并使用我的初始(可能设计糟糕的哈希函数),得到了非常奇怪的行为: #包括 #包括 #包括 #包括 #包括 常数int N=3;//阵列的维数 std::数组getRandomArray(){ //引擎和分布保持状态,因此定义为静态 静态std::默认\u随机\u引擎e;//引擎 静态std::均匀实分布d(0,1);//分布 std::数组ret; 对于(尺寸i=0;i
#包括
#包括
#包括
#包括
#包括
常数int N=3;//阵列的维数
std::数组getRandomArray(){
//引擎和分布保持状态,因此定义为静态
静态std::默认\u随机\u引擎e;//引擎
静态std::均匀实分布d(0,1);//分布
std::数组ret;
对于(尺寸i=0;i std::cout您的程序显示未定义的行为(未初始化std::size\u t ret
变量除外)
首先,ArrayEqual
不会产生等价关系。它是不可传递的-存在三个数组a
、b
和c
,使得a
与b
足够接近,b
与c
足够接近,但a
不够接近到c
其次,ArrayHash
可能不会为声明相等的两个数组返回相同的哈希值
这两个都是std::unordered_set
的模板参数的先决条件。您是否检查了给定数组的实际哈希值是多少?使用-O3构建时是否会发生变化?是否查看了一些可管理数量的随机数组的哈希值分布?是否-O3选择了不同的默认随机引擎?std::size\u t ret;
你意识到事情是不确定的吗?因此后续循环的ret+=std::hash()(elem);
几乎一文不值。所以,是的,我会说它设计得很糟糕。@WhozCraig谢谢,真的是这样(我感到羞耻)!初始化std::size\u t ret=0后,我去掉了第二个奇怪的东西。谢谢,我完全同意你的两个观点。现在的实际问题是如何修复它们。你需要定义等价类,并坚持使用它们。例如,你可以将空间分成一个N维网格,1e-6
单位在一边。所有点如果落入同一单元格,则被ArrayEqual
视为等效。为类选择一个代表(例如,单元格的中心或具有最小坐标的角),并让ArrayHash
为单元格中的每个点返回该代表的哈希值。
#include <iostream>
#include <chrono>
#include <random>
#include <array>
#include <unordered_set>
const int N = 3; // Dimensionality of the arrays
std::array<double, N> getRandomArray() {
// Engines and distributions retain state, thus defined as static
static std::default_random_engine e; // engine
static std::uniform_real_distribution<double> d(0, 1); // distribution
std::array<double, N> ret;
for (size_t i = 0; i < N; ++i) {
ret[i] = d(e);
}
return ret;
}
// Return Squared Euclidean Distance
template <typename InputIt1, typename InputIt2>
double EuclideanDistance2(InputIt1 beg1, InputIt1 end1, InputIt2 beg2) {
double val = 0.0;
while (beg1 != end1) {
double dist = (*beg1++) - (*beg2++);
val += dist*dist;
}
return val;
}
struct ArrayHash { // Hash Function
std::size_t operator() (const std::array<double, N>& arr) const {
std::size_t ret = 0;
for (const double elem : arr) {
ret += std::hash<double>()(elem);
}
return ret;
}
};
struct ArrayEqual { // Equivalence Criterion
bool operator() (const std::array<double, N>& arr1,
const std::array<double, N>& arr2) const {
return EuclideanDistance2(arr1.begin(), arr1.end(), arr2.begin()) < tol*tol;
}
private:
static constexpr double tol = 1e-6; // Comparison tolerance
};
int main() {
// create a unordered set of double arrays (usda)
std::unordered_set<std::array<double, N>, ArrayHash, ArrayEqual> usda;
// record start time
auto start = std::chrono::steady_clock::now();
// Generate and insert one hundred thousands new double arrays
for (size_t i = 0; i < 100000; ++i) {
// Get a new random double array (da)
std::array<double, N> da = getRandomArray();
usda.insert(da);
}
// record finish time
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to generate and insert unique elements into UNORD. SET: "
<< diff.count() << " s\n";
std::cout << "unord. set size() = " << usda.size() << std::endl;
return 0;
}