from bisect import bisect_left
def take_closest(myList, myNumber):
"""
Assumes myList is sorted. Returns closest value to myNumber.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
if pos == 0:
return myList[0]
if pos == len(myList):
return myList[-1]
before = myList[pos - 1]
after = myList[pos]
if after - myNumber < myNumber - before:
return after
else:
return before
$ python -m timeit -s "
from closest import take_closest
from random import randint
a = range(-1000, 1000, 10)" "take_closest(a, randint(-1100, 1100))"
100000 loops, best of 3: 2.22 usec per loop
$ python -m timeit -s "
from closest import with_min
from random import randint
a = range(-1000, 1000, 10)" "with_min(a, randint(-1100, 1100))"
10000 loops, best of 3: 43.9 usec per loop
如果不确定列表是否已排序,则可以使用built-in ^{} function 来查找与指定数字的最小距离的元素。
注意,它还可以与带int键的dict一起使用,比如
{1: "a", 2: "b"}
。这种方法需要O(n)时间。如果列表已经排序,或者您可以只对数组进行一次排序,请使用@Lauritz's answer中所示的等分方法,该方法只需要O(log n)时间(注意,检查列表是否已经排序是O(n),排序是O(logn)。)
lambda是编写“匿名”函数(没有名称的函数)的一种特殊方式。因为lambda是一个表达式,所以可以为它指定任何名称。
写上述内容的“长”方法是:
我将重命名函数
take_closest
,以符合PEP8命名约定。如果您的意思是快速执行,而不是快速编写,} 几乎总是更快。
min
应该而不是是您选择的武器,除非在一个非常狭窄的用例中。min
解决方案需要检查列表中的每个数字,并对每个数字进行计算。而使用^{“几乎”来自这样一个事实:需要对列表进行排序才能工作。希望您的用例是这样的,您可以对列表进行一次排序,然后就不用管它了。即使不需要,只要每次调用
take_closest
之前不需要排序,bisect
模块很可能会出现在顶部。如果你有疑问,试试两者,看看现实世界的差异。“对分”的工作原理是重复地将一个列表减半,并通过查看中间值来找出必须包含的那一半
myNumber
。这意味着它的运行时间为O(log n),而不是highest voted answer的运行时间O(n)。如果我们比较这两种方法并用排序后的myList
提供这两种方法,则会得到以下结果:所以在这个特殊的测试中,
bisect
几乎快了20倍。对于较长的列表,差异将更大。如果我们通过取消
myList
必须排序的先决条件来平衡竞争环境呢?假设我们每次调用take_closest
时都对列表的副本进行排序,而不改变min
解决方案。使用上述测试中的200个项目列表,bisect
解决方案仍然是最快的,尽管只有大约30%。这是一个奇怪的结果,考虑到排序步骤是O(n log(n))!
min
仍然失败的唯一原因是,排序是在高度优化的c代码中完成的,而min
必须为每个项费力地调用lambda函数。随着myList
大小的增加,min
解决方案最终会更快。请注意,我们必须把所有有利于它的东西放在一起,才能使min
解决方案获胜。相关问题 更多 >
编程相关推荐