使用浮点数作为哈希表的键是否安全?

时间:2010-08-03 17:52:57

标签: language-agnostic floating-point hashtable

我需要存储成对的float,int int值,其中float值存储在我用于我正在开发的工具的模型中的{{1}}值的出现次数我想知道做这些事情是否安全。

当谈论用于直接比较的浮点数(或作为要散列的内容)时,有限精度应该是一个问题所以我认为不鼓励类似的方法,我是对的吗?

实际上问题是我没有任何其他信息与这些浮点数相结合所以我根本不能使用其他任何东西作为散列表的键,但同时,因为键将是很多,有一个良好的表现会很好。

也许最好的解决方案是使用二进制搜索树(或更高级的数据结构)来获得至少O(logn)的平均情况,如果常数因子更好

你有什么建议吗?只是为了让你知道我正在开发OCaml,但我认为这些考虑因素可以被认为是与语言无关的

4 个答案:

答案 0 :(得分:7)

浮点数的常见问题是计算是近似的。如果您以两种不同的方式计算相同的值,结果可能会略有不同。 (在某些情况下,通过以相同的方式计算相同的值,可以获得轻微的差异。)

因此,如果您对浮点数进行任何计算,您将获得近似值,而不应依赖于相等性。如果您的来源以各种方式计算浮点数,则传递给您的数据将是近似值。如果您获得精确的浮点值,并且可以指望任何应该相同的数字是完全相同的位表示,那么相等就像正常一样工作,并且您可以使用哈希表。

答案 1 :(得分:5)

我认为这里有几个问题

使用浮点数作为哈希表的键是否安全?

是。我现在想不到一种语言floats不符合哈希表中密钥所需的要求(通常是稳定的哈希码和相等语义)

是否可以使用包含大量密钥的哈希表?

取决于多少。如果键的数量如此之大,则会导致表超出允许的内存大小,然后肯定没有,因为它会导致内存不足的情况。没有更多的背景,回答这部分问题真的是不可能的。可能你是唯一一个能够回答它的人。

float的精确度是否比int等其他类型更差?

这是特定于实现的,但我相信OCaml float具有双精度(8字节)。因此,询问精度是否使其无效作为键是等同于询问C#long类型不适合作为哈希表键。它们都具有相同数量的可能值(它们都是8个字节)。我当然会说long是一种有效的密钥类型(经常使用它并且没有任何问题)。

我认为真正的问题是你是否不负责任地创建float的实例作为密钥使用。

如果我的哈希表内存不足,那么二叉树会更好吗?

可能但不是很多。二叉树和哈希表都涉及开销。对于哈希表,它通常是未使用的桶和桶内列表中的下一个指针。对于二叉树,树中的每个元素都有2个额外的开销(左右指针)。如果你的内存不足,我不确定是否会更好地转向二叉树。

答案 2 :(得分:1)

您是在谈论性能问题还是有效性问题?

对于有效性:如果要计算相同浮点数的出现次数,则没有问题。如果你想计算大致相同的浮点数的出现次数,你需要弄清楚“大致相同”的含义。

答案 3 :(得分:1)

如果您确定要计算精确浮点值的实例数,那么您可能没问题。

正如David所说,关键字浮点数的哈希表的固有问题是哈希表使用相等来识别键,并且由于计算错误,浮点数的相等性是一个稍微不可靠的概念。 sin(pi / 6) == 0.5甚至(2.0 / 3) * (2.0 / 3) == (4.0 / 9)都没有一般保证。在这两种情况下,LHS可能与RHS有点或更不同。

因此,如果您计算的某些条目输入为0.5,而某些条目计算为sin(pi / 6),并且您希望将它们计算在一起,那么您需要做的不仅仅是只是在浮点值上哈希。

你可能会躲避舍入然后散列,尽管你永远不会完全逃避这个问题。例如,如果你舍入到最接近的0.001,那么你将0.2020001和0.2020003识别为“相同的值,有计算错误”,但不是同等接近的0.1014999和0.1015001。我使用了base-10示例来简化输入,但当然“float”通常意味着二进制表示。

完全相同的问题适用于二叉树。 Hashtables并不关心他们的关键数据“是什么”,他们只关心有人可以提供将键映射到整数的函数h,这样任何xy你都可以想要考虑“平等”,h(x) == h(y)。然后,为了提高性能,您希望h不再引入“碰撞”(h(x) == h(y) x != y的实例),而不是随机机会。用浮子来做这件事没有任何障碍。您必须确保不在哈希中包含任何不参与比较的内容,如果您包含参与比较的所有信息,这将有所帮助。

如果你能解决你实际上在计算什么的问题,那么这可能会引导你进入你需要的数据结构。如果你确实想要在匹配中有一些容差,你可能最好对所有浮点数进行排序,然后寻找值的集群。