03最小假币问题的Python实现
假设由29枚硬币,其中一枚假币,假币比真币重,有一个量程和精度足够的天平,怎样保证最少称重次数找出假币?
思路:
将硬币分为3堆,则每堆的硬币数量为 n/3 ,但是这是在 n%3==0 的情况下才能成立,所以我们将 n 枚硬币分为 3 堆加 1 堆 余数堆(余数堆可能为0),则可分为如下(n-n%3)/3, (n-n%3)/3, (n-n%3)/3, n%3。
如下分组:
a堆: (n-n%3)/3
b堆: (n-n%3)/3
c堆: (n-n%3)/3
d(余数堆): n%3
逻辑过程:
1.判断n中的硬币数量,如果n>2则执行2,否则执行4.
2.将n分为上图的四堆,拿 a 和 b 比较,如果 a > b ,则假币在 a 中。否则假币在b中。
3.如果 a == b,则拿 a 和 c 比较。如果 a == c,则假币在d(余数堆)中。将 d 再次执行流程1,并且n=n%3。如果不等,则假币在 c 中,将 c 再次 执行流程1,并且n=(n-n%3)/3。
4.如果n==2,则将两枚硬币进行比较找出假币。
5.如果n==1,则该硬币就是假币,输出结果结束
代码:
# -*- coding: utf-8 -*- """ Created on Sun Oct 18 20:23:22 2020 @author: chenj """ 'list[start:end] 列表截取 包含start,不包含end' # 导入 random(随机数) 模块 import random #称重次数 times=0 '分组' def fenzu (coins): n=len(coins) #如果硬币数量是两个则分成2组 if n==2: group_a=coins[0 : 1] group_b=coins[1 : n] group_c=[] group_d=[] else: # // 取整除-向下取接近商的整数 "/"就表示 浮点数除法,返回浮点结果;"//"表示整数除法。 #也就是说 / 返回的是浮点类型的结果, 而 // 返回的是整数结果(可以理解为/的整数部分)。 group_a=coins[0 : n//3] group_b=coins[n//3 : n//3*2] group_c=coins[n//3*2 : n//3*3] group_d=coins[n//3*3 : n] #余数堆 #输出分组的结果与分组的个数 print('group_a:',group_a,'group_b:',group_b,'group_c:',group_c,'group_d:',group_d) print('group_a_len:',len(group_a),'group_b_len:', len(group_b),'group_c_len:',len(group_c),'group_d_len:',len(group_d)) return group_a,group_b,group_c,group_d '两个组重量大小比较' def chengzhong (group_a,group_b): if sum(group_a) > sum(group_b): result=('left') elif sum(group_a) < sum(group_b): result=('right') else: result =('equal') return result '返回假币的列表组' def find_fake(group_a,group_b,group_c,group_d): res=chengzhong (group_a,group_b) if res=='left': return group_a elif res=='right': return group_b #如果a组和b组相等 则比较a和c elif res=='equal': #如果d组存在,也就是有4组情况下,要比较2次才能确定c组是否为错误堆 if len(group_d)>=1: res = chengzhong(group_a, group_c) global times #使用全局变量 times += 1 print('times:',times) if res == 'equal': #如果a=b=c返回d组(余数堆) print('a=b=c') return group_d else: #如果不相等则c为错误堆 print('a=b!=c') return group_c #不存在d组直接返回c组 else: return group_c '硬币的比较主要函数' def bianlifind(coins): global times #使用全局变量 search_list=coins while len(search_list)> 1: print('search_list'+str(times),search_list,'search_list_len',len(search_list)) if len(search_list)==2: #如果列表长度为2 g1,g2,g3,g4=fenzu(search_list) search_list=find_fake(g1,g2,g3,g4) times += 1 print('times:',times) print("search_list==2") else: g1,g2,g3,g4=fenzu(search_list) search_list=find_fake(g1,g2,g3,g4) times += 1 print('times:',times) fake=search_list[0] #列表只有一个时直接取出假币 #python的print字符串前面加f表示格式化字符串, #加f后可以在字符串里面使用用花括号括起来的变量和表达式, #如果字符串里面没有表达式,那么前面加不加f输出应该都一样. print(f'错误的硬币的重量{fake}') #list.index(obj)从列表中找出某个值第一个匹配项的索引位置(0开始) print('这个重硬币是在原始硬币的第', coins.index(fake)+1, '个位置') print(f'称重次数{times}') coinsCount = int(input("请输入硬币的个数: ")) coins=[] #定义硬币列表 for i in range(coinsCount): coins.append(1) ## 使用 append() 添加元素 '定义一组硬币' #coins=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] # 随机生成假币的位置 coinNum = random.randint(0, len(coins)-1) # 假币在那个位置 那个位置就为2 不在为1 coins[coinNum] = 2 bianlifind(coins)