分治算法-找假币问题-2分法VS3分法
找假币问题-2分法 VS 3分法
问题:有k枚硬币,其中有一枚假币,假币与真币无外观差异,只是假币的重量稍微大一些。现有一个无砝码称重天平,通过称重的方式找出假币,求最小称重次数
解题思路
暴力求解
求解过程:拿一枚硬币放在天平一头,再不断往另一头放硬币,每次放一枚,直到找出那一枚偏重的硬币
计算次数:n = k-1 (k>=2)
2分法
求解过程:(1)将所有硬币整除2进行分组(硬币分成2组,总数为奇数时,多出的一个硬币单独一组);(2)比较两组硬币重量,重量大的组别中含有假币,若两组硬币重量相同则单独一组的那一个硬币为假币。重复(1)(2)两个步骤。
计算次数:
n
≈
l
o
g
2
k
n \approx log_2{k}
n≈log2k次(k>=2)
3分法
求解过程:(1)将所有硬币整除3进行分组(硬币分成3组,前2组硬币数量相同,第3组数量大于前2组);(2)比较第1、2两组硬币重量,重量大的组别中含有假币,若两组硬币重量相同则假币在第3组中。重复(1)(2)两个步骤;当硬币数量小于3则分为2组,重量大的组别中含有假币
计算次数:
n
≈
l
o
g
3
k
n \approx log_3{k}
n≈log3k次(k>=2)
效果对比
也不用我哔哔,这个对比很明显了。
自己的逗逼想法
用4分法、5分法、6分法。。。。。。能让求解步骤更少吗?
想了挺久的,发现并不能,原因在于天枰只有2头可以放硬币。优化方向应该是”让每次不用称重就可做判断的硬币数量“尽量大,而3分法在这个场景下已经是最优了。4分即是2分,6分可以当成2个3分、3个2分,5分可以可以当成是2分与3分的组合,更高分,也类似。比较一下要么等价于3分法,要么求解次数大于3分法。
用数学的方式表达一下,求解次数n,硬币k个,k = a + b
n = log 3 a + log 2 b n = \log_3{a} + \log_2{b} n=log3a+log2b
代码
2分法
import numpy as np
def compare_group(group_list):
'''
比较各组别的硬币总重量,返回重量最大的组别
param : 例如[[1.0,1.0],[1.0,1.0],[1.0]]
'''
if sum(group_list[0]) > sum(group_list[1]):
return group_list[0]
elif sum(group_list[0]) < sum(group_list[1]):
return group_list[1]
else :
return None
def group(coin_list):
'''
分组并返回重量最大的组别
param : 例如[1.0,1.0,1.0,1.0,1.1,1.0,1.0]
'''
coin_num = len(coin_list)
if coin_num >= 2 :
group_1 = coin_list[0:coin_num // 2]
group_2 = coin_list[coin_num // 2:coin_num//2 * 2]
group_3 = coin_list[coin_num // 2 * 2:]
compare_value = compare_group([group_1,group_2])
if compare_value :
return compare_value
else :
return group_3
def weighed(coin_list):
'''
循环执行分组、取最大重量组别
param : 例如[1.0,1.0,1.0,1.0,1.1,1.0,1.0]
'''
weighed_coin_list = coin_list.copy()
weighed_num = 0
while True :
if len(weighed_coin_list) < 2 :
break
weighed_coin_list = group(weighed_coin_list)
weighed_num = weighed_num + 1
return weighed_num,weighed_coin_list[0]
if __name__ == '__main__':
list1 = [1.15] #定义假币
ones1 = list(np.ones(20000))
coin_list = ones1 + list1 + ones1
weighed_num,fake_money_weight = weighed(coin_list)
print("weighed_num:",weighed_num,
"fake_money_weight:",fake_money_weight)
3分法
import numpy as np
def compare_group(group_list):
'''
比较各组别的硬币总重量,返回重量最大的组别
param : 例如[[1.0,1.0],[1.0,1.0],[1.1,1.0,1.0]]
'''
if sum(group_list[0]) > sum(group_list[1]):
return group_list[0]
elif sum(group_list[0]) < sum(group_list[1]):
return group_list[1]
else :
return group_list[2]
def group(coin_list):
'''
分组并返回重量最大的组别
param : 例如[1.0,1.0,1.0,1.0,1.1,1.0,1.0]
'''
coin_num = len(coin_list)
#print(coin_num)
if coin_num >= 3 :
group_1 = coin_list[0:coin_num // 3]
group_2 = coin_list[coin_num // 3:coin_num//3 * 2]
group_3 = coin_list[coin_num // 3 * 2:]
return compare_group([group_1,group_2,group_3])
elif coin_num == 2 :
group_1 = coin_list[0:coin_num // 2]
group_2 = coin_list[coin_num // 2 :]
return compare_group([group_1,group_2])
def weighed(coin_list):
'''
循环执行分组、取最大重量组别
param : 例如[1.0,1.0,1.0,1.0,1.1,1.0,1.0]
'''
weighed_coin_list = coin_list.copy()
weighed_num = 0
while True :
if len(weighed_coin_list) < 2 :
break
weighed_coin_list = group(weighed_coin_list)
weighed_num = weighed_num + 1
return weighed_num,weighed_coin_list[0]
if __name__ == '__main__':
list1 = [1.15] #定义假币
ones1 = list(np.ones(20000))
coin_list = ones1 + list1 + ones1
weighed_num,fake_money_weight = weighed(coin_list)
print("weighed_num:",weighed_num,
"fake_money_weight:",fake_money_weight)