假设假币比真币轻,使用分2堆称重和分3堆称重的方法分别找出假币,同时比较两种方法的称重次数。
产生含假币的金币堆
def genCoins(coinNum, fakeCoin):
coins = [1] * coinNum
coins[fakeCoin] = 0
return coins
称出一堆金币重量
def weighCoins(coins, beg, end):
s = 0
for i in range(beg, end):
s += coins[i]
return s
分2堆找出
def weighBy2(coins, beg, end, times=0):
times += 2
# 只剩一个返回
if end - beg == 1:
print "weigh times: %s" % times
return beg
cnum = end - beg
f = cnum / 2
w1 = weighCoins(coins, beg, beg + f)
w2 = weighCoins(coins, beg + f, beg + f * 2)
# 继续称较轻的一堆
if w1 < w2:
return weighBy2(coins, beg, beg + f, times)
elif w1 > w2:
return weighBy2(coins, beg + f, beg + f * 2, times)
else:
# 相等时就是最后一个
return end-1
def weighFakeCoinBy2(coins):
return weighBy2(coins, 0, len(coins))
分3堆找出
def weighFakeCoinBy3(coins):
return weighBy3(coins, 0, len(coins))
def weighBy3(coins, beg, end, times=0):
times += 2
if end - beg == 1:
print "weigh times: %s" % times
return beg
# if coins num is not even,plus 1 coin before beg or after end
cnum = end - beg
f = cnum / 3
if f == 0:
f = 1
w1 = weighCoins(coins, beg, beg + f)
w2 = weighCoins(coins, beg + f, beg + f * 2)
if w1 < w2:
return weighBy3(coins, beg, beg + f, times)
elif w1 > w2:
return weighBy3(coins, beg + f, beg + f * 2, times)
else:
return weighBy3(coins, beg + f * 2, end, times)
测试代码
class test(unittest.TestCase):
def testGenCoins(self):
fakeCoin = 9
coinNum = 10
coins = genCoins(coinNum, fakeCoin)
for i in range(coinNum):
if i == fakeCoin:
self.assertEqual(coins[i], 0, "testGenCoins failed")
else:
self.assertEqual(coins[i], 1, "testGenCoins failed")
def testWeigh(self):
fakeCoin = 2
coinNum = 3000000
self.assertEqual(weighFakeCoinBy2(genCoins(coinNum, fakeCoin)), fakeCoin)
self.assertEqual(weighFakeCoinBy3(genCoins(coinNum, fakeCoin)), fakeCoin)
if __name__ == '__main__':
unittest.main()
分2堆的复杂度log2N,3堆是log3N。分3堆更快的原因在于比分2堆更快地减小的问题的规模。因为分为3堆后,只要比较一次,如果2小堆相等,则假币在第3堆中;如果不等,假币就在质量较大的一堆中。