ChatGPT解决这个技术问题 Extra ChatGPT

从整数列表中,获取最接近给定值的数字

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

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

有什么快速的方法可以做到这一点吗?

还返回在列表中发生的索引怎么样。
@sancho.s 很好发现。尽管这个问题的答案比其他问题的答案要好得多。所以我将投票关闭另一个作为这个副本的副本。

C
Community

如果我们不确定列表是否已排序,我们可以使用 built-in min() function 来查找与指定数字距离最小的元素。

>>> min(myList, key=lambda x:abs(x-myNumber))
4

请注意,它也适用于具有 int 键的 dicts,例如 {1: "a", 2: "b"}。此方法需要 O(n) 时间。

如果列表已经排序,或者您可以只对数组进行一次排序,请使用 @Lauritz's answer 中说明的二等分方法,它只需要 O(log n) 时间(但请注意,检查列表是否已经排序是 O (n) 并且排序是 O(n log n)。)


说起来很复杂,这就是 O(n),对 bisect 稍加修改就会大大改进 O(log n)(如果您的输入数组已排序)。
@mic_e:这只是 Lauritz's answer
还返回在列表中发生的索引呢?
@CharlieParker 创建您自己的 min 实现,通过字典 (items()) 而不是列表运行它,最后返回键而不是值。
或者使用 numpy.argmin 而不是 min 来获取索引而不是值。
1
15 revs, 3 users 97%

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

如果您的意思是快速执行而不是快速编写,那么 min 应该是您选择的武器,除非在一个非常狭窄的用例中。 min 解决方案需要检查列表中的每个数字对每个数字进行计算。改用 bisect.bisect_left 几乎总是更快。

“几乎”来自 bisect_left 需要对列表进行排序才能工作的事实。希望您的用例是这样的,您可以对列表进行一次排序,然后不理会它。即使没有,只要您不需要在每次调用 take_closest 之前进行排序,bisect 模块很可能会排在首位。如果您有疑问,请尝试两者并查看现实世界的差异。

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),而 highest voted answer 的运行时间为 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

所以在这个特定的测试中,bisect 快了近 20 倍。对于更长的列表,差异会更大。

如果我们通过删除 myList 必须排序的先决条件来平衡竞争环境会怎样?假设我们每次调用 take_closest 时都会对列表的副本进行排序,同时保持 min 解决方案不变。使用上述测试中的 200 项列表,bisect 解决方案仍然是最快的,尽管只有大约 30%。

这是一个奇怪的结果,考虑到排序步骤是 O(n log(n))min 仍然失败的唯一原因是排序是在高度优化的 c 代码中完成的,而 min 必须为每个项目调用一个 lambda 函数。随着 myList 大小的增加,min 解决方案最终会更快。请注意,为了让 min 解决方案获胜,我们必须将所有内容都对其有利。


排序本身需要 O(N log N),所以当 N 变大时它会变慢。例如,如果您使用 a=range(-1000,1000,2);random.shuffle(a),您会发现 takeClosest(sorted(a), b) 会变慢。
@KennyTM 我会同意你的,我会在我的回答中指出这一点。但是,只要每次排序都可以多次调用 getClosest,这会更快,并且对于一次排序的用例,这是不费吹灰之力的。
还返回在列表中发生的索引呢?
如果 myList 已经是 np.array,则使用 np.searchsorted 代替 bisect 会更快。
如果我想返回的不是关闭值,而是它的 ID,该怎么办?
B
Burhan Khalid
>>> takeClosest = lambda num,collection:min(collection,key=lambda x:abs(x-num))
>>> takeClosest(5,[4,1,88,44,3])
4

lambda 是编写“匿名”函数(没有名称的函数)的一种特殊方式。您可以为其指定任何您想要的名称,因为 lambda 是一个表达式。

编写上述内容的“长”方式是:

def takeClosest(num,collection):
   return min(collection,key=lambda x:abs(x-num))

但是请注意,根据 PEP 8,不鼓励将 lambda 分配给名称。
G
Gustavo Lima
def closest(list, Number):
    aux = []
    for valor in list:
        aux.append(abs(Number-valor))

    return aux.index(min(aux))

此代码将为您提供列表中最接近 Number 的索引。

KennyTM 给出的解决方案总体上是最好的,但是在你不能使用它的情况下(比如 brython),这个功能会起作用


n
nbro

遍历列表并将当前最接近的数字与 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

你也可以返回索引。
!不正确!应该是 if abs(myList[i] - myNumber) < abs(closest - myNumber): closest = myList[i];。不过最好事先存储该值。
当然,现在的函数已经返回最接近的索引。为了满足 OP 的要求,倒数第二行不应该是最接近的 = myList[i]
佚名
def find_nearest(array, value):
    array = np.asarray(array)
    idx = (np.abs(array - value)).argmin()
    return array[idx]

通过使用运行它

price_near_to=find_nearest(df['Close'], df['Close'][-2])

j
jmdeamer

需要注意的是,Lauritz 关于使用 bisect 的建议思想实际上并没有在 MyList 中找到与 MyNumber 最接近的值。相反,bisect 在 MyList 中的 MyNumber 之后按顺序查找下一个值。因此,在 OP 的情况下,您实际上会返回 44 的位置而不是 4 的位置。

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

要获得最接近 5 的值,您可以尝试将列表转换为数组并像这样使用 numpy 中的 argmin。

>>> import numpy as np
>>> myNumber = 5   
>>> myList = [1, 3, 4, 44, 88] 
>>> myArray = np.array(myList)
>>> pos = (np.abs(myArray-myNumber)).argmin()
>>> myArray[pos]
...
4

我不知道这会有多快,我的猜测是“不是很”。


Lauritz 的功能正常工作。您只使用 bisect_left ,但 Lauritz 建议使用函数 takeClosest(...) 进行额外检查。
如果要使用 NumPy,可以使用 np.searchsorted 而不是 bisect_left。 @Kanat 是对的 - Lauritz 的解决方案 确实 包含选择两个候选者中哪个更接近的代码。
J
JayJay123

扩展古斯塔沃利马的回答。无需创建全新列表即可完成相同的操作。随着 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.

u
umn

如果我可以添加到 @Lauritz's answer

为了不出现运行错误,不要忘记在 bisect_left 行之前添加条件:

if (myNumber > myList[-1] or myNumber < myList[0]):
    return False

所以完整的代码看起来像:

from bisect import bisect_left

def takeClosest(myList, myNumber):
    """
    Assumes myList is sorted. Returns closest value to myNumber.
    If two numbers are equally close, return the smallest number.
    If number is outside of min or max return False
    """
    if (myNumber > myList[-1] or myNumber < myList[0]):
        return False
    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

S
Sawndes
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)

请提供一些解释。