找出較輕的那個球

2021-09-26 04:38:53 字數 3596 閱讀 6926

@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'分析區間: : ')

# 不多於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'第次比較,分成區間:, , ')

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'分析區間: : ')

# 不多於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'第次比較,分成區間:, , ')

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'輕球是:, 比較次數:')

count = 0

print(f'輕球是:, 比較次數:')

找出那個程序造成的死鎖

use master gocreate procedure sp who lock asbegin declare spid int,bl int,inttransactioncountonentry int,introwcount int,intcountproperties int,intcou...

12個球找出奇異球

問題 有12個球,有乙個是奇異的球,其他都一樣,有乙個天平,只能稱3次,找出這個球。答案 1 將球分成等分 2 四個四個稱重,如果相同,則在另外四個中存在奇異球,將這奇怪的球為 a b c d 另外8個為標準球 efghi。a b 和c d 稱重,若a b 大於 c d,a c 和 e d稱重,若 ...

找出落單出現的那個數字

題目描述 乙個陣列除了某乙個數字只有乙個外,其餘的數字都是成對出現的 求出這個落單的數字 思路 使用異或的特性,可以抵消陣列中相同的數字,則剩下的哪個數字就是落單的數字 示例 乙個陣列除了某乙個數字只有乙個外,其餘的數字都是成對出現的 public class unique number integ...