【问题标题】:How to handle hash collisions?如何处理哈希冲突?
【发布时间】:2024-04-11 11:10:01
【问题描述】:

我正在开发一款游戏,其中游戏世界中的每一件事都由一个全球唯一标识符表示。

这些 id 每个都是 64 位,是通过将创建时间、机器网络地址和随机数哈希在一起生成的。根据*在Birthday problem 上的文章,两亿条记录发生哈希冲突的概率为 0.1%。

由于我不太可能获得那么多记录,因此可以认为没有哈希值会发生冲突。但我不希望这样,而是让我的应用程序处理罕见的 id 冲突情况,即哈希冲突。

否则,这种行为将是非常不受欢迎的,因为游戏世界中两个独立的事物会产生联系,从而共享它们的位置、运动、生命值等属性。

如何处理哈希冲突?通常如何处理它们?

【问题讨论】:

  • 实际上,人们通常确实假设 GUID 永远不会发生冲突。
  • 听起来你想要的并不是真正的哈希,而是一个唯一的标识符。有什么理由不使用 128 位 GUID?
  • @bara 我想使用像 unsigned long long int 这样的 C++ 标准类型而不是数组来存储 id。而且,我没有那么多记录。但无论如何,问题仍然存在于任何 id 长度。
  • @danijar 然后我会回到“为什么使用哈希?”的问题。你真正想要的是一个唯一的 id,除非有理由不这样做(比如 id 是以分布式方式生成的)。
  • @bara 我需要身份证,对。但是由于数据可以从不同的独立机器(保存游戏、修改、补丁、插件)加载,所以这个 id 必须是全局唯一的。所以这是一种哈希,我猜,对吧?

标签: hash guid identifier hash-collision robustness


【解决方案1】:

通常以两种方式处理哈希冲突:

  1. 使用更大的散列,这样几乎不可能发生冲突。

  2. 考虑哈希码是非唯一的,对实际数据使用相等比较器来确定唯一性。

128 位 GUID 使用第一种方法。 .NET 中的HashSet<T> 类就是第二种方法的一个例子。

【讨论】:

  • 如果 128 位 GUID 发生冲突怎么办?
  • @danijar:对于 260 亿条记录,其概率为 0.0000000000000001%。这与一些 GUID 在内存电路中自发改变的概率大致相同。
  • 我仍然不能使用 128 位。这是有多种原因的。例如,像 std::unordered_map 这样的 stl 容器只接受 std::size_t 的哈希值,它测量 64 位。使用较大的 id 将需要再次对它们进行散列以获得较小的地图散列,这是无用的,因为这是主要用例。能否请您详细说明如何consider hash codes to be non-unique
  • @danijar:第二个选项只是接受可能存在哈希冲突。对于散列实际发生冲突的罕见情况,您只需比较实际数据以查看它是否是相同的值。
  • 好的,然后我必须扫描加载的数据并将所有对碰撞哈希的引用更新为新生成的引用。可能实际问题是关于这个过程的,但我认为这非常依赖于实现,以至于它过于本地化了。