具有3D均匀网格,为了在大型模型中节省内存,不需要保存空单元格(不与任何对象重叠的单元格)。为了这个目的,我在c#中使用Dictionary。尽管性能已经降低,但这仍然比创建3D网格时出现异常更好。现在我的问题是找到一个快速哈希函数,将网格的3d整数坐标映射到唯一的数字。
我已经尝试过((x * 73856093 + y * 19349669 + z * 83492791))%n,它并不总是生成一个唯一的数字。
答案 0 :(得分:4)
一方面,您将目标写为“保存记忆”,另一方面,您要求“快速哈希函数将网格的3d整数坐标映射到唯一数字”。这两者不太兼容。
要么保证 O(1)访问权限。在这种情况下,您必须防止哈希冲突,并且必须将输入映射到唯一数字。但在这种情况下,您还需要哈希映射中与可能的输入一样多的单元格。因此,您可以通过简单的N×N×N阵列节省内存。
或者 - 这更有可能 - 您只希望哈希冲突很少见。然后你可以得到一个大约是实际存储对象数量两倍的哈希映射。但在这种情况下,您不必完全避免哈希冲突,您只需要使它们足够罕见。
选择一个好的哈希函数很大程度上取决于输入数据的可能模式。如果输入相当随机,并且知道哈希映射的大小,则应该以均匀分布为目标。如果对象更可能位于相邻块中,那么您需要确保坐标中的微小变化不太可能导致碰撞。这是有助于不使你的因素成为主要因素的点,因此一个方向上的微小变化不太可能在另一个方向上发生碰撞。
如果有疑问,您可以随时测试:给定三个素数(例如,对于散列137x + 149y + 163z)和一些真实世界设置(即使用的坐标和生成的哈希映射大小),您可以简单地应用哈希到所有坐标,mod到哈希映射大小并计算唯一值的数量。为各种三元组做同样的事情,并选择最大化这个数字的三元组。但我怀疑这种优化程度是否值得付出努力。
答案 1 :(得分:3)
请参阅有关哈希函数的wikipedia文章,而不是尝试在已经很好的主题上撰写新文章。特别是第一张图像清楚地显示了多个输入如何被散列到相同的值。
基本上,你的三元组被散列到[0,2 ^ 64 - 1]范围内的某个散列值(允许重复!)。然后通过等式hash = hash%n将范围减小到略大于输入值的数量(比如n = 5)。对于[(1,1,1),(1,2,3),(2321,322,232),(3,3,3)]的输入值的结果关系将看起来像这样:
(1,1,1) -> 2
(1,2,3) -> 0
(2321, 322, 232) -> 0
(3,3,3) -> 3
正如您所看到的,没有任何输入值与1或4相关(即散列),并且有两个输入值散列为0。
通过注意为了从哈希表中检索输入值(例如(1,1,1)),可以明确哈希的功效(以及平均情况为O(1)的原因)步骤发生。
hash = hash % n
,因此(1,1,1) - > 2。hash_function[2] = (1,1,1) + additional data stored with this particular input value
。如果多个输入值映射到相同的散列值(在我们的示例中为0),则内部算法需要对那些通常使用红黑树进行的输入值进行搜索(最坏情况) O(log n)
)。因此,任何查找的最坏情况也是O(log n)
。
当关系成为函数一对一(双射)时,就会发生完美哈希。这提供了最佳性能,但很少见。正如我之前所说的,幸运的是,很容易产生一个几乎完美的哈希,其中重复是稀缺的。实质上使您的哈希函数尽可能随机。
我在评论中给出的例子可能是足够的(而且做错的方法):)但更标准的计算方法是:hash = ((((prime1 + value1) * prime2) + value2) * prime3) + value3) * prime4
也回答了这个问题。请注意,素数可以是任何素数,但通常在实践中使用小值,如31,37等。
在实践中,测试可用于检查性能,但通常不是必需的。
无论如何重新阅读你的问题我想知道你为什么不放弃整个哈希想法而不只是将你的点存储在一个简单的数组中?