@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...