魔术弹跳球问题

时间:2011-08-24 23:51:29

标签: algorithm math

  玛丽为她的生日买了一个魔法球。球被抛出时   一些高度,弹跳为这个高度的两倍。玛丽甩了   球从她的阳台上x高出地面。帮助她   计算球到达时需要多少弹跳   身高w

     

输入:一个整数z(1≤z≤10 6 )作为测试用例的数量。对于   每个测试,整数xw(1≤x≤10 9 ,0≤w≤10 9 )。

     

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

好吧,所以,虽然它看起来难以言喻,但我找不到一种更有效的方法来解决它,而不是一个简单,愚蠢,残酷的循环方法,将x乘以2直到它至少{{ 1}}。当然,对于最大限度的测试,它将获得可怕的时间。然后,我想过使用以前的案例节省了相当多的时间,条件是我们可以在短时间内(O(1)?)从前面的案例中得到最接近但更小的结果,但是,我不能(并且不要不知道是否可能..)实施。该怎么做?

2 个答案:

答案 0 :(得分:3)

您基本上是在尝试解决问题

  

2 i x = w

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

  

2 i = w / x

     

i = log 2 (w / x)

因此,一种方法是明确计算此值,然后采用上限。当然,在执行此操作时,您必须注意数值不稳定性。例如,如果您使用float s对值进行编码,然后让w = 8,000,001和x = 1,000,000,您将得到错误的答案(3而不是4)。如果您使用double来保存该值,当x = 1且w = 536870912时,您也会得到错误的答案(报告30而不是29,因为1 x 2 29 = 536870912 ,但由于双重不准确,答案被错误地舍入到30)。看起来我们不得不改用不同的方法。

让我们重温一下你的初始解决方案,即将x的值加倍,直到超过w,这里应该完全没问题。 log 2 (w / x)给出了双倍x到达w的最大次数,由于w / x最多为10亿,因此迭代最多为log 2 10 9 次,每次约30次。进行30次乘以2的迭代可能会非常快。更一般地,如果w / x的上限是U,那么这将花费最多O(log U)时间来完成。如果要检查k(x,w)对,则需要时间O(k log U)。

但是,如果你对此不满意,那么你可以尝试另一种非常快速的算法。实质上,您希望计算log 2 w / x。您可以通过创建一个列出所有2的幂及其对数的表来开始。例如,您的表可能看起来像

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

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

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

希望这有帮助!

答案 1 :(得分:2)

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

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

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

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

修改
在使用此方法之前,您应该检查w&gt;如果不是,则返回1。 (球必须至少反弹一次)。

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