【问题标题】:Is it safe to use floats as keys of hashtables?使用浮点数作为哈希表的键是否安全?
【发布时间】:2010-08-03 17:52:56
【问题描述】:

我需要存储float,int 对,其中int 值存储float 值在我正在用于我正在开发的工具的模型中的出现次数,我想知道它是否是安全地做这样的事情..

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

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

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

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

【问题讨论】:

    标签: language-agnostic floating-point hashtable


    【解决方案1】:

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

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

    【讨论】:

    • “在某些情况下,您可以通过两次相同的方式计算相同的值来获得细微的差异。”这些是什么情况?
    • @Alec:如果编译器在一个操作实例中使用寄存器,而在另一个实例中使用内存,则您可以从精度差异中得到不同的结果。 x86 FP 有 80 位寄存器,但存储在 32 位和 64 位中,之前四舍五入。所以你可以让 cos(23) == cos(23) 返回 false (这实际上发生在我身上)。
    • 一个简单的解决方法是在比较之前将结果存储在变量中。
    【解决方案2】:

    我认为这里有几个问题

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

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

    有很多键的哈希表可以吗?

    取决于有多少。如果键的数量如此之多,它会导致表扩展超过允许的内存大小,那么肯定不会,因为它会导致内存不足的情况。如果没有更多上下文,真的不可能回答这部分问题。可能只有你能回答这个问题。

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

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

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

    如果我用哈希表耗尽内存,二叉树会更好吗?

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

    【讨论】:

      【解决方案3】:

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

      为了有效性:如果您想计算相同浮点数的出现次数,那么没有问题。如果您想计算近似相同的浮点数的出现次数,您需要弄清楚“近似相同”对您意味着什么。

      【讨论】:

        【解决方案4】:

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

        正如 David 所说,hashtable keyed on floats 的内在问题是 hashtables 使用相等来识别键,并且由于计算错误,float 的相等性是一个稍微不可靠的概念。没有一般保证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 示例,但当然“浮点”通常表示二进制表示。

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

        如果您能够解决实际计数的问题,那么这可能会引导您找到所需的数据结构。如果您确实希望在匹配中具有一定的容差,则最好对所有浮点数进行排序,然后查找值簇。

        【讨论】:

          猜你喜欢
          • 2017-12-10
          • 2015-05-11
          • 2014-05-30
          • 2013-04-24
          • 2020-03-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多