【问题标题】:Providing a good hash function提供良好的哈希函数
【发布时间】:2016-12-07 13:46:04
【问题描述】:

在我最初的问题(详细的实验调查)中:Appropriate container for the fast insertion and lookup of n-dimensional real vectors (initial benchmarking provided) 我的初始问题(可能是设计不佳的哈希函数)使用未排序集管理随机 N 维浮点数组时遇到了非常奇怪的行为:

#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;
}

最奇怪的两件事是:

  1. 即使使用宽松的容差 (1e-1),也可以在没有任何优化标志的情况下运行实验,几乎所有随机向量(实现为 N 维数组)都被识别为唯一的。我没有使用vectorssets 观察到这一点。
  2. 打开 -O3 优化标志时,唯一元素的数量与未优化的数量显着不同,这肯定表明我的方法有问题。

编辑:考虑到@WhozCraig 的评论,第二个问题已解决。

所以,我的问题是:这是不是因为我的哈希函数设计不当而导致的奇怪行为?如果是这样,您能否建议如何为我的案例制作更好的哈希函数?

【问题讨论】:

  • 您是否检查过给定数组的实际哈希值是多少?使用 -O3 构建时它会改变吗?您是否查看过一些可管理数量的随机数组的哈希值分布? -O3 会选择不同的默认随机引擎吗?
  • std::size_t ret; 你意识到事情是 indeterminate 吗?因此,后续循环的ret += std::hash&lt;double&gt;()(elem); 几乎毫无价值。所以,是的,我会说它设计得很糟糕。
  • @WhozCraig 谢谢,情况确实如此(我感到羞耻)!初始化std::size_t ret = 0 后,我摆脱了第二个奇怪的东西。

标签: c++ stl performance-testing unordered-set hash-function


【解决方案1】:

您的程序表现出未定义的行为(未初始化的std::size_t ret 变量除外)。

首先,ArrayEqual 不会产生等价关系。它不具有传递性 - 存在三个数组 abc,因此 abb“足够接近”,而 c 和 @987654330 则“足够接近” @ 与 c 不够“接近”。

其次,ArrayHash 可能不会为ArrayEqual 声明相等的两个数组返回相同的哈希值。

这两个都是 std::unordered_set 模板参数的先决条件。

【讨论】:

  • 谢谢,我完全同意你的两个说法。现在的实际问题是如何解决它们。
  • 您需要定义您的等价类,并坚持使用它们。例如。您可以将空间划分为 N 维网格,一侧为1e-6 个单位。 ArrayEqual 将所有落入同一单元格的点视为等效点。为该类选择一个代表(例如单元格的中心,或坐标最小的角),并让 ArrayHash 返回单元格中每个点的该代表的哈希值。
猜你喜欢
  • 1970-01-01
  • 2010-11-22
  • 2011-10-27
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多