找假币问题-2分法 VS 3分法

分治算法-找假币问题-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} nlog2k次(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} nlog3k次(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)
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页