【问题标题】:Fast lookup suffering from floating point inaccuracies浮点不准确的快速查找
【发布时间】:2015-04-11 19:15:52
【问题描述】:

假设我有等间距的双精度数(64 位浮点数)x0,x1,...,xn。等间距意味着对于所有ix(i+1) - xi 是恒定的;称它为w 的宽度。

给定y[x0,xn] 范围内的一个数字,我想找到最大的i 使得xi <= y

一种天真的方法会依次访问每个i (O(n))。稍微好一点的是使用二分搜索 (O(log n))。

恒定时间查找是计算(y-x0)/w 并将其转换为整数。但是,由于浮点不准确,这有时会给出错误的结果。例如。假设从 0 开始有 100 个宽度为 0.01 的区间。

(int)(0.29/0.01) = 28 //want 29 here

我能否保留恒定时间查找但确保结果始终与二分查找相同?用小数执行计算,而不是双倍的 'w' 和 'x0' 似乎在这里工作,但它总是工作吗?我总是可以通过直接查找来与任一侧的xs 进行比较,但这看起来很丑陋且效率低下。

澄清一下 - 我被赋予了 xi 和值 y 作为双打 - 我无法改变这一点。但是在返回整数索引之前执行的任何中间计算都可以使用我喜欢的任何数据类型。此外,我可以执行一次性的“准备”工作,以加快运行时计算。

编辑:道歉 - 事实证明我没有正确检查“等距” - 当使用浮点算术计算它们的差异时,这些数字通常不是“等距”。

【问题讨论】:

  • (int)(0.29/0.01 + 0.5) = 28 如果你想对它进行舍入而不是截断。 (0.289999/0.01 = 28.999999,截断时为 28)
  • 1) 舍入而不是截断会降低需要调整的可能性。 (int)Math.Round(0.29/0.01) 2) 我会使用索引计算,然后使用一个循环来调整它,通过向上/向下步进来调整它,直到你处于正确的位置。对于足够病态的病例,一步调整可能还不够。 3) C# 的浮点规范非常邪恶,除非您在正确的位置从双精度转换为双精度(强制将精度降低为双精度),否则上述算法可能仍然无法始终工作。
  • @iKiWiXz 最好明确并使用 Math.Round、Math.Floor、Math.Ceiling,而不是试图强迫演员这样做
  • @PanagiotisKanavos 你是对的,但我并不想给出答案,我只是想解释一下原因。
  • 如果你有 N 个被 W 等间隔的双精度,你确定你需要存储实际的双精度,而不仅仅是每个的索引,这样xi = index * W?那么解决方案将变得微不足道,不是吗?

标签: c# algorithm floating-point floating-accuracy


【解决方案1】:

equally spaced 是什么意思?如果可以对数字做出一些假设,例如 - 它们在某个区间内增加,您实际上可以使用中值选择,即在最佳情况下为 O(1),在最坏情况下为 O(log2(N))。

【讨论】:

  • 等间距意味着计算为x(i+1) - xi 的双精度是恒定的,与i 无关。
【解决方案2】:

执行以下操作

计算(int)(0.29/0.01) = 28 //want 29 here

接下来,计算 28-1 和 28+1 之间的 i 的 i * 0.01 并选择正确的。

【讨论】:

  • 是的,这基本上是我在提到比较 x 的任一侧时所暗示的。如果可能的话,我想避免这种情况。
  • 您说“我可以保留恒定时间查找但确保结果始终与二进制搜索相同吗?” - 这似乎可以肯定地回答您的问题。
  • 您只需要查看 3 个值。那是恒定的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 2015-09-07
  • 2011-01-07
  • 2019-11-28
相关资源
最近更新 更多