给定一个整数列表,我想找到哪个数字最接近我输入的数字:

>>> myList = [4, 1, 88, 44, 3]
>>> myNumber = 5
>>> takeClosest(myList, myNumber)
...
4

有什么快速的方法吗?


当前回答

def closest(list, Number):
    aux = []
    for valor in list:
        aux.append(abs(Number-valor))

    return aux.index(min(aux))

这段代码将为您提供列表中与number最接近的数字的索引。

KennyTM给出的解决方案是最好的,但在您不能使用它的情况下(如brython),这个函数将完成工作

其他回答

扩展Gustavo Lima的回答。不用创建一个全新的列表也可以完成同样的事情。随着FOR循环的进行,列表中的值可以替换为差值。

def f_ClosestVal(v_List, v_Number):
"""Takes an unsorted LIST of INTs and RETURNS INDEX of value closest to an INT"""
for _index, i in enumerate(v_List):
    v_List[_index] = abs(v_Number - i)
return v_List.index(min(v_List))

myList = [1, 88, 44, 4, 4, -2, 3]
v_Num = 5
print(f_ClosestVal(myList, v_Num)) ## Gives "3," the index of the first "4" in the list.

我将重命名函数take_nearest以符合PEP8命名约定。

如果你指的是快速执行而不是快速编写,那么min不应该是你选择的武器,除非在一个非常狭窄的用例中。最小解需要检查列表中的每个数字,并对每个数字进行计算。使用平分。而Bisect_left几乎总是更快。

“几乎”来自于bisect_left需要对列表进行排序才能工作的事实。希望您的用例是这样的,您可以对列表进行一次排序,然后不去管它。即使不是这样,只要您不需要在每次调用take_nearest之前进行排序,那么平分模块很可能会排在最前面。如果你有疑问,两种都试一下,看看现实世界的区别。

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

Bisect的工作原理是将列表重复分成两半,并通过查看中间值来找出myNumber必须位于哪一半。这意味着它的运行时间为O(log n),而投票最多的答案的运行时间为O(n)。如果我们比较这两个方法,并为它们提供一个排序后的myList,结果如下:

$ 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

在这个测试中,平分法几乎快20倍。对于较长的列表,差异会更大。

如果我们通过移除myList必须排序的前提条件来创造一个公平的竞争环境呢?假设每次调用take_nearest时,我们对列表的一个副本进行排序,同时保持最小解不变。使用上述测试中的200项列表,平分解决方案仍然是最快的,尽管只快了大约30%。

这是一个奇怪的结果,考虑到排序步骤是O(nlog (n))!min仍然失败的唯一原因是排序是在高度优化的c代码中完成的,而min必须为每一项调用lambda函数。随着myList规模的增长,最小解最终会更快。请注意,为了让最小解获胜,我们必须把一切都叠在对它有利的地方。

def takeClosest(myList, myNumber):
    newlst = []
    for i in myList:
        newlst.append(i - myNumber)
    lstt = [abs(ele) for ele in newlst]
    print(myList[lstt.index(min(lstt))])

myList = [4, 1, 88, 44, 3]
myNumber = 5
takeClosest(myList,myNumber)
def closest(list, Number):
    aux = []
    for valor in list:
        aux.append(abs(Number-valor))

    return aux.index(min(aux))

这段代码将为您提供列表中与number最接近的数字的索引。

KennyTM给出的解决方案是最好的,但在您不能使用它的情况下(如brython),这个函数将完成工作

遍历列表并将当前最接近的数字与abs(currentNumber - myNumber)进行比较:

def takeClosest(myList, myNumber):
    closest = myList[0]
    for i in range(1, len(myList)):
        if abs(i - myNumber) < closest:
            closest = i
    return closest