【问题标题】:Magic bouncing ball problem魔术弹跳球问题
【发布时间】:2011-08-24 23:51:28
【问题描述】:

玛丽的生日得到了一个魔法球。球,当从 某个高度,反弹到这个高度的两倍。玛丽扔了 来自她阳台上的球,在地面以上x。帮助她 计算球需要反弹多少次才能到达 身高w.

输入:一个整数z (1 ≤ z ≤ 106) 作为测试用例的数量。为了 每个测试,整数 xw (1 ≤ x ≤ 109, 0 ≤ w ≤ 109)。

输出:对于每种情况,一个整数等于反弹次数 需要打印球到达w

好的,所以,虽然它看起来非常简单,但我找不到比简单、愚蠢、残酷的循环方法更有效的方法来解决它,即循环将 x 乘以 2 直到它至少为 w。当然,对于最大的测试,这将是一个可怕的时间。然后,我想使用以前的案例,这样可以节省很多时间,前提是我们可以在短时间内从以前的案例中获得最接近但更小的结果(O(1)?),但是,我不能(并且不要'不知道是否有可能..)实施。这应该怎么做?

【问题讨论】:

  • 不妨试试对数。
  • 你听说过logarithms吗? (但由于在给定的限制下,跳出的次数最多为 30 次,因此朴素算法并没有什么“可怕”的地方)。
  • 当然你只需要使用对数。 e: 太慢了
  • @Hammerite,我花了一些时间才明白你的意思并不是说 natural 对数会太慢 :-)

标签: algorithm math


【解决方案1】:

您实际上是在尝试解决问题

2i x = w

然后找到大于 i 的最小整数。求解,我们得到

2i = w / x

i = log2 (w / x)

因此,一种方法是明确计算此值,然后取上限。当然,这样做时你必须注意数值的不稳定性。例如,如果您使用floats 对值进行编码,然后让 w = 8,000,001 和 x = 1,000,000,您最终会得到错误的答案(3 而不是 4)。如果你使用doubles 来保存这个值,你也会在 x = 1 和 w = 536870912 时得到错误的答案(报告 30 而不是 29,因为 1 x 229 = 536870912,但是由于双精度数不准确,答案被错误地四舍五入到 30)。看来我们必须换一种方法了。

让我们重新审视一下您最初的解决方案,即只需将 x 的值加倍直到它超过 w 就可以了。您可以将 x 翻倍直到达到 w 的最大次数由 log2 (w/x) 给出,并且由于 w/x 最多为十亿,因此最多迭代 log 2 109 次,每次约 30 次。进行 30 次乘以 2 的迭代可能会非常快。更一般地说,如果 w / x 的上限是 U,那么这将最多需要 O(log U) 时间来完成。如果你有 k (x, w) 对要检查,这需要时间 O(k log U)。

不过,如果您对此不满意,可以尝试另一种非常快速的算法。本质上,您想要计算 log2 w/x。您可以从创建一个表格开始,其中列出了 2 的所有幂及其对数。例如,您的表格可能看起来像

T[1] = 0
T[2] = 1
T[4] = 2
T[8] = 3
...

然后您可以计算 w/x,然后进行二分搜索以找出值在哪个范围内。这个范围的上限是球必须反弹的次数。这意味着如果您有 k 个不同的对要检查,并且如果您知道 w/x 的最大比率为 U,则创建此表需要 O(log U) 时间,然后每个查询所花费的时间与大小的 log 成正比表,即 O(log log U)。整体运行时间为 O(log U + k log log U),非常好。假设您最多处理一百万个问题实例,而 U 是十亿,k log log U 不到五百万,log U 大约是三十。

最后,如果你愿意用 bitwise hackery 做一些非常糟糕的事情,因为你知道 w/x 适合 32 位字,你可以使用 this bitwise trickery with IEEE doubles 在极少数机器操作中计算对数。这可能会比上述两种方法更快,但我不一定能保证。

希望这会有所帮助!

【讨论】:

  • 好的,我想我得到了这个......非常感谢! :)
  • +1 预计算。或者更确切地说,根据需要进行计算,然后存储在字典中。
  • 你期望从日志中得到什么样的数值问题?真的没有理由不这样做 - 首先检查参数是否大于零。绝对没有必要搞乱制表值。只是做数学。
  • @andrew cooke- 我不确定你是否需要担心。我担心的是在某些情况下,您获取日志并返回像 2.000000001 而不是 2.0 这样的值,因此最终会给出错误的答案(因为上限将返回 3 而不是 2,所以您错了反弹次数)。这可能永远不会出现,但我非常怀疑使用对数和浮点数在所有情况下都能正常工作,而没有看到证明您不需要担心的证明或示例程序。跨度>
  • 好的。我删除了之前的回复,因为它似乎太挑剔了——这种事情对我来说似乎太过分了,但我想这只是意见分歧。
【解决方案2】:

使用此公式计算每个测试用例的跳出次数。

ceil( log(w/x) / log(2) )

这是伪代码,但将其转换为任何语言都应该非常简单。只需将 log 替换为在某个特定基数中查找数字的对数的函数,并将 ceil 替换为将给定十进制值四舍五入到其上方的下一个 int 的函数(例如,ceil(2.3) = 3)。

请参阅 http://www.purplemath.com/modules/solvexpo2.htm 了解其工作原理(在您的情况下,您正在尝试求解方程 x * 2 ^ n = w 的整数 n,并且您应该首先将两边除以 x)。

编辑:
在使用此方法之前,您应该检查 w > x,如果不是,则返回 1。 (球总是必须至少反弹一次)。

此外,有人指出,浮点值的不准确可能会导致此方法有时会失败。您可以通过检查是否 2 ^ (n-1) >= w 来解决此问题,其中 n 是上述等式的结果,如果是则返回 (n - 1) 而不是 n。

【讨论】:

  • 不幸的是,如果您使用 IEEE 浮点值,由于数字中可能出现不准确,这将无法正常工作。有关详细信息,请参阅我上面的答案。
猜你喜欢
  • 2013-10-28
  • 2013-05-14
  • 2013-01-13
  • 2013-05-23
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多