提问人:Blue 提问时间:11/4/2023 最后编辑:mkrieger1Blue 更新时间:11/15/2023 访问量:506
尝试在列表中查找第二个最大值时出错
Error when trying to find 2nd maximum value in a list
问:
我正在尝试编写代码来查找列表的第二个最大值。
我是这样试的:
arr = map(int, input().split())
lista = list(arr)
max_value = lista[0]
run = lista[0]
for i in lista:
if max_value < i:
max_value = i
for j in lista:
if run < j and run < max_value:
run = j
print(run)
第二个最大值和最大值是相同的。我的程序中有什么错误?
答:
问题
顶级域名
run = lista[0]
...
if run < j and run < max_value:
应该是
v
run = min(lista)
...
v
if run < j and j < max_value:
如何找到问题
1. 让代码更简单
理解而不是映射 - 列表构造函数继承
我们可以通过使用理解而不是连续的映射和列表构造函数来减少第一行的样板:
arr = map(int, input().split())
lista = list(arr)
成为:
lista = [int(n) for n in input().split()]
向 Python 询问最大值,不要自己动手
逻辑的下一个块是在这里找到列表的最大值。 你不需要自己写这段代码,你可以问 Python。这将降低代码的复杂性,使问题/错误更容易被发现。
max_value = lista[0]
for i in lista:
if max_value < i:
max_value = i
成为:
max_value = max(lista)
重命名变量
为了帮助我们的大脑理解发生了什么,让我们澄清变量名称:
lista = [int(n) for n in input().split()]
max_value = max(lista)
run = lista[0]
for j in lista:
if run < j and run < max_value:
run = j
print(run)
成为:
user_input = [int(n) for n in input().split()]
max_value = max(user_input)
second_max = user_input[0]
for current_value in user_input:
if second_max < current_value and second_max < max_value:
second_max = current_value
print(second_max)
2. 找到问题
现在,代码更小,更易于理解,出错的地方更少。如果有错误,我们不必看很多地方。
怎么了? 应该是第二个最大值,但事实并非如此。
是什么原因导致的?不应该发生更新。second_max
previous_value
所以这就是问题可能发生的地方。
if second_max < current_value and second_max < max_value:
second_max = current_value
归因正确,条件应该错误。
这似乎是正确的,因为我们只想在低于 current_value 时进行更新(这意味着 current 可能是真实值或等于 。
所以我们需要另一个条件:不应该是 ,否则,可以设置为 .second_max < current_value
second_max
second_max
max_value
current_value
max_value
second_max
max_value
而且,我们看一下第二个条件:。这是我们的错误。second_max < max_value
让我们修复条件,因为它应该低于 。
此外,如果第一个值是最大值,则需要将初始值设置为最小值。current_value
max_value
second_max
user_input = [int(n) for n in input().split()]
max_value = max(user_input)
second_max = min(user_input)
for current_value in user_input:
if second_max < current_value and current_value < max_value:
second_max = current_value
print(second_max) # 55
做。
备选方案:使用 set、sorted 和 index
如果您想要列表中的第二个最大值,则更容易对重复的列表进行排序,并在列表的索引 1 处打印元素,例如第二个元素。
分步示例
without_duplicates = {int(n) for n in input().split()}
ordered_without_duplicates = sorted(without_duplicates, reverse=True)
print(ordered_without_duplicates[1])
Oneliner 示例
print(sorted({int(n) for n in input().split()}, reverse=True)[1])
使用 heapq
print(heapq.nlargest(2, {int(n) for n in input().split()})[1])
评论
= min()
第二个循环中的测试不正确,请使用以下命令:
arr = map(int, input().split())
lista = list(arr)
max_value = lista[0]
run = lista[0]
for i in lista:
if max_value < i:
max_value = i
for j in lista:
if run<j and j!=max_value:
run = j
print(run)
选中的值 () 不应等于 。j
max_value
def second_max(arr):
first=second=0
for i in arr:
if i> first:
second=first
first=i
elif i> second:
second=i
return second
评论
first=second=0
first=second=arr[0]
arr
有趣的是,与其他常用方法相比,您的方法(带有逻辑校正)非常快。它在下面的基准中。f1
这是一个在线基准测试。
如果您想在自己的计算机上尝试,请执行以下代码:
import random
import time
def f1(lista):
max1,max2 = [lista[0]]*2
for i in lista:
if max1 < i:
max1 = i
for j in lista:
if max2<j and j!=max1:
max2 = j
return max1,max2
def f2(lista):
max1=max(lista)
return max1,max(set(lista)-{max1})
def f3(lista):
return tuple(sorted(set(lista))[-2:][::-1])
def f4(lista):
max1 = max(lista)
max2 = max(lista, key=lambda x: min(lista)-1 if (x == max1) else x)
return max1,max2
def f5(lista):
max1=max(lista)
max2=max(x for x in lista if x < max1)
return max1,max2
def f6(lista):
import heapq
heap = [(-x, x) for x in lista]
heapq.heapify(heap)
_,max1=heapq.heappop(heap)
_,max2=heapq.heappop(heap)
return max1,max2
def f7(lista):
max1 = max(lista[0], lista[1])
max2 = min(lista[0], lista[1])
n = len(lista)
for i in range(2,n):
if lista[i] > max1:
max2 = max1
max1 = lista[i]
elif lista[i] > max2 and max1 != lista[i]:
max2 = lista[i]
elif max1 == max2 and max2 != lista[i]:
max2 = lista[i]
return max1,max2
def f8(lista):
max1=max2=lista[0]
for e in lista:
if e > max1:
max2=max1
max1=e
elif e > max2:
max2=e
return max1,max2
def cmpthese(funcs, args=(), cnt=1000, rate=True, micro=True):
"""Generate a Perl style function benchmark"""
def pprint_table(table):
"""Perl style table output"""
def format_field(field, fmt='{:,.0f}'):
if type(field) is str: return field
if type(field) is tuple: return field[1].format(field[0])
return fmt.format(field)
def get_max_col_w(table, index):
return max([len(format_field(row[index])) for row in table])
col_paddings=[get_max_col_w(table, i) for i in range(len(table[0]))]
for i,row in enumerate(table):
# left col
row_tab=[row[0].ljust(col_paddings[0])]
# rest of the cols
row_tab+=[format_field(row[j]).rjust(col_paddings[j]) for j in range(1,len(row))]
print(' '.join(row_tab))
results={}
for i in range(cnt):
for f in funcs:
start=time.perf_counter_ns()
f(*args)
stop=time.perf_counter_ns()
results.setdefault(f.__name__, []).append(stop-start)
results={k:float(sum(v))/len(v) for k,v in results.items()}
fastest=sorted(results,key=results.get, reverse=True)
table=[['']]
if rate: table[0].append('rate/sec')
if micro: table[0].append('\u03bcsec/pass')
table[0].extend(fastest)
for e in fastest:
tmp=[e]
if rate:
tmp.append('{:,}'.format(int(round(float(cnt)*1000000.0/results[e]))))
if micro:
tmp.append('{:,.1f}'.format(results[e]/float(cnt)))
for x in fastest:
if x==e: tmp.append('--')
else: tmp.append('{:.1%}'.format((results[x]-results[e])/results[e]))
table.append(tmp)
pprint_table(table)
if __name__=='__main__':
lista=[random.randint(-1e6,1e6) for _ in range(100000)]
funcs=[f1,f2,f3,f4,f5,f6,f7,f8]
for f in funcs: print(f'{f.__name__}: {f(lista)}')
cmpthese(funcs, [lista])
在最近的 Mac 上,这会打印:
f1: (999991, 999978)
f2: (999991, 999978)
f3: (999991, 999978)
f4: (999991, 999978)
f5: (999991, 999978)
f6: (999991, 999978)
f7: (999991, 999978)
f8: (999991, 999978)
rate/sec μsec/pass f3 f6 f2 f4 f5 f7 f1 f8
f3 104 9,656.6 -- -3.1% -11.4% -45.9% -61.6% -71.2% -82.9% -87.3%
f6 107 9,358.3 3.2% -- -8.6% -44.1% -60.3% -70.3% -82.3% -86.9%
f2 117 8,557.1 12.8% 9.4% -- -38.9% -56.6% -67.5% -80.7% -85.7%
f4 191 5,227.9 84.7% 79.0% 63.7% -- -29.0% -46.8% -68.3% -76.5%
f5 269 3,712.2 160.1% 152.1% 130.5% 40.8% -- -25.1% -55.4% -66.9%
f7 360 2,779.1 247.5% 236.7% 207.9% 88.1% 33.6% -- -40.4% -55.9%
f1 604 1,655.3 483.4% 465.3% 416.9% 215.8% 124.3% 67.9% -- -25.9%
f8 815 1,226.9 687.1% 662.8% 597.5% 326.1% 202.6% 126.5% 34.9% --
评论
答案无法解决列表以最大值开头的异常。为了解决这个问题,
lista = [int(n) for n in input().split()]
max_value = max(lista)
for a in lista:
if a != max_value:
run = a
for j in lista:
if run<j and j!=max_value:
run = j
print(run)
其次,两者都有效。for
j!=max_value
j<max_value
我会这样写代码:
from heapq import nlargest
from typing import List
def second_largest(values: List[int]) -> int:
result = nlargest(2, set(values))
return result[1]
拆分函数后,代码更具可测试性。与发帖人代码相似的调用方如下所示:
def second_largest_helper(value_str: str) -> int:
return second_largest(map(int, value_str.split()))
如果传入的列表中有单个唯一值或没有值,则代码将引发 IndexError 异常。
以下是我测试过的简单测试数据:
if __name__ == '__main__':
test_data = [
([2, 2, 3, 5, 4, 6, 5, 3, 7], 6),
([2, 2, 3, 5, 4, 6, 5, 3], 5),
([2, 2, 3, 5, 4, 5, 3], 4),
([2, 2, 3, 4, 5, 3], 4),
([2, 2, 3, 4, 3], 3),
([2, 2, 3, 3], 2),
([2, 2, 3], 2),
]
for data, expected in test_data:
print(f"{data = } {expected = }")
assert second_largest(data) == expected
2023-11-14
您可以为下面的基准测试代码编写如下代码:
def second_largest(values: List[int]) -> int:
return nlargest(2, set(values))
或者对于重复项很少的数据集,如下所示:
def second_largest(values: List[int]) -> int:
return nlargest(2, values)
根据基准测试代码,第二种实现是迄今为止最快的替代方案,运行速度比 f8 快 66%。
评论