n个小球放入m个盒子中_用两个同等强度的小球测试摔碎所需的最低楼层的最优策略是什么呢?...

题目:

给定两个强度一样小球(自由落体摔碎需要的最小楼层数相同), 现有一个层高100的大楼, 可以选择从任意楼层抛小球, 小球一旦摔碎, 就无法继续用于测试, 测出小球摔碎所需最低楼层的最优策略是什么呢? 这里最优策略是指最差情况下, 抛小球的次数最小化.

显然最笨的办法有两种:

  1. 用一个小球, 从1楼到100楼实验. 最差情况下, 需要100层.
  2. 第一个小球选择从地50层抛下, 最差情况下, 需要51次.

一般大家会想到, 选择层号为10的整数倍的楼层, 最差情况下, 19次.

显然这不是最优结果.

只少还有两种选择楼层的方法:

方法1最差需要17次.

# -*- coding: utf-8 -*
from math import ceil,sqrt
cache={}
# 第一个小球, 等差插值结果不是最优.
# 第1个测试位置是10
# 第2个测试位置是90的平方根, 约等于10
# 第三个: 80的平方根, 9
# ...
def f(n, k):
    key="%d_%d" % (n, k)
    if key in cache:
        return cache[key];

    if k==0:
        cache[key]=n
        return cache[key]
    if k==1:
        cache[key]=ceil(sqrt(n))
        return cache[key]
    else:
        cache[key]=ceil(sqrt(n-sum([f(n,i) for i in range(1,k)])))
        return cache[key]


xs=[f (100, k) for k in  range(1,30)]
xs=filter(lambda x:x>0, xs)
layers=map(lambda i:sum(xs[0:i]), range(1, len(xs)+1))
print layers
i_xs=zip(range(1,len(xs)+1), xs)
print i_xs
ns=[a+b for (a,b) in i_xs]
print ns
# 最差情况下, 线上扫描, 不必测试本轮的最后一个楼层, 可以省一个小球.
print "max=%d" % (max(ns)-1)

可以选择16个楼层:

10, 20, 29, 38, 46, 54, 61, 68, 74, 80, 85, 89, 93, 96, 98, 100

而另外一种方法, 目前笔者得到*最优的策略是14*

方法如下:

from math import ceil

ks=range(0,20)
xs=[ (k, ceil((100.0-k*(k+1)/2)/(k+1))) for k in ks]
xs=filter(lambda x: x[1]>0, xs)
xs=map(lambda x:(x[0], x[0]+x[1]),xs)
print xs

可以选择从其中12个楼层抛小球

14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100

显然第一个小球在前11个选出的楼层的某个楼层摔碎的话, 最差情况下都需要14次. 在最后一个选出的楼层(100层)抛小球, 只需要12层.

有没有更好的算法呢?

从计算复杂性理论或者信息熵角度, 能否估算出最优算法的最差时间复杂度呢? 能否严格证明14是最优解?

===========

@心月狐 在评论中给出了最优结果14的简单优美的证明, 抄录如下:

14次是最少的!

如果我们试验13次,以0代表为未碎1代表碎,可以写出“试验序列”。

如0000000010000,000010001(0000)

将长度不足13的序列后面用0补齐,那么所有可能的实验结果恰好对应长度为13且其中有0~2个1的序列。

所有试验结果的总数是

13C2 + 13C1.+ 13C0

= 78 + 13 + 1

= 92

但总情况数有100(101)种,所以在最坏的情况下是无法确定的。至少要14次才能保证。

==========

完美

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值