找出较轻的那个球

'''

@Description: In User Settings Edit

@Author: your name

@Date: 2019-08-14 10:59:05

@LastEditTime: 2019-08-14 16:47:34

@LastEditors: Please set LastEditors

给定n个球,其中只有1个球比其他球轻,请问至少比较多少次,可以找出该轻球?

两种思路:

算法1:

易知当n=1时,比较次数为0;当n=2或者3时,比较次数为1。

故可以将n个球尽可能平分成3堆,然后比较其中数量相等的2堆,若其中有一堆较轻,则轻球在其中;

若参与比较的两堆等重,则轻球在第3堆中。

按照相同的规则继续判断存在轻球的那一堆,直到找到轻球为止。

可以用递归函数实现本算法,每次递归时,取3堆球中数量最多的一堆,其值为((n + 2) // 3)。

算法2:

设比较次数为m,分析可知当n=1时,m=0;当n<=3^1时,m=1;当n<=3^2时,m=2;

以此类推,当3^(i-1) < n <= 3^i时,m=i。

可以构造一个3^i的列表,然后将n的值与列表元素比较,找出对应的m值。

具体的操作方法是将n个球分成3堆,当3^(m-1) < n <= 3^m时,先取出3^(m-1)个球作为第一堆,剩下的球若能平分成2堆则直接比较;

否则从第一堆中取出1个放到较少的那堆,使得第2、3堆球数量相等,然后比较第2、3堆球。

若轻球在第1堆,则可直接判断比较次数为1+(m-1);否则轻球在第2或3堆,因为第2、3堆球的数量不多于第1堆,

故最大比较次数不会大于m次。

'''

# -*- coding: utf-8 -*-

 

import random


 

def fun_1(n):

if n <= 1:

return 0

elif n in (2, 3):

return 1

else:

return 1 + fun_1((n + 2) // 3)


 

def fun_2(n):

a = [3**i for i in range(20)]

i = 0

while n > a[i]:

i += 1

return i

 

#模拟算法1的比较过程,并输出轻球的序号

def select_1(a, L, R):

print(f'分析区间:{L} : {R}')

# 不多于3个球,可以直接给出解

if L == R:

print('无需比较')

return L

global count

count += 1

if L + 1 == R:

if a[L] < a[R]:

return L

else:

return R

elif L + 2 == R:

if a[L] == a[L+1]:

return R

elif a[L] < a[L+1]:

return L

else:

return L+1

n = (R-L+1+2) // 3 # 第一堆球的数量

m = R - L + 1 - n # 剩下两堆球的数量

if m % 2 == 1: # 从第一堆球中拿出一个以便后两堆球能平分

n -= 1

m += 1 # 剩下两堆球的数量增1

r1 = (L, L+n-1) # 第一堆球所在区域

r2 = (L+n, L+n+m//2-1) # 第二堆球所在区域

r3 = (L+n+m//2, R) # 第三堆球所在区域

print(f'第{count}次比较,分成区间:{r1}, {r2}, {r3}')

if sum(a[r2[0]:r2[1]+1]) == sum(a[r3[0]:r3[1]+1]):

return select_1(a, r1[0], r1[1])

elif sum(a[r2[0]:r2[1]+1]) < sum(a[r3[0]:r3[1]+1]):

return select_1(a, r2[0], r2[1])

else:

return select_1(a, r3[0], r3[1])

 

#模拟算法2的比较过程,并输出轻球的序号

def select_2(a, L, R):

def num(n):

i = 0

while n > 3**i:

i += 1

return 3**(i-1)

 

print(f'分析区间:{L} : {R}')

# 不多于3个球,可以直接给出解

if L == R:

print('无需比较')

return L

global count

count += 1

if L + 1 == R:

if a[L] < a[R]:

return L

else:

return R

elif L + 2 == R:

if a[L] == a[L+1]:

return R

elif a[L] < a[L+1]:

return L

else:

return L+1

 

n = num(R-L+1) # 第一堆球的数量

m = R - L + 1 - n # 剩下两堆球的数量

if m % 2 == 1: # 从第一堆球中拿出一个以便后两堆球能平分

n -= 1

m += 1 # 剩下两堆球的数量增1

r1 = (L, L+n-1) # 第一堆球所在区域

r2 = (L+n, L+n+m//2-1) # 第二堆球所在区域

r3 = (L+n+m//2, R) # 第三堆球所在区域

print(f'第{count}次比较,分成区间:{r1}, {r2}, {r3}')

if sum(a[r2[0]:r2[1]+1]) == sum(a[r3[0]:r3[1]+1]):

return select_2(a, r1[0], r1[1])

elif sum(a[r2[0]:r2[1]+1]) < sum(a[r3[0]:r3[1]+1]):

return select_2(a, r2[0], r2[1])

else:

return select_2(a, r3[0], r3[1])

 

# for i in range(1, 30):

# print(i, fun_1(i), fun_2(i))

 

# s = 0

# for i in range(1, 300000):

# if fun_1(i) != fun_2(i):

# s += 1

# print(s)


 

n = 30

a = [2] * n

p = random.randrange(n)

a[p] = 1

print(p, a)

count = 0

print(f'轻球是:{select_1(a, 0, n-1)}, 比较次数:{count}')

count = 0

print(f'轻球是:{select_2(a, 0, n-1)}, 比较次数:{count}')

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值