从整数列表中,获取最接近给定值的数字
Posted
技术标签:
【中文标题】从整数列表中,获取最接近给定值的数字【英文标题】:From list of integers, get number closest to a given value 【发布时间】:2012-08-21 21:24:33 【问题描述】:给定一个整数列表,我想找出哪个数字最接近我在输入中给出的数字:
>>> myList = [4, 1, 88, 44, 3]
>>> myNumber = 5
>>> takeClosest(myList, myNumber)
...
4
有什么快速的方法吗?
【问题讨论】:
如何同时返回列表中发生这种情况的索引。 finding index of an item closest to the value in a list that's not entirely sorted的可能重复 @sancho.s 很好发现。尽管这个问题的答案比其他问题的答案要好得多。所以我要投票关闭另一个作为这个副本的副本。 【参考方案1】:遍历列表并将当前最接近的数字与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]【参考方案2】:
如果我们不确定列表是否已排序,我们可以使用built-in min()
function,找到与指定数字距离最小的元素。
>>> min(myList, key=lambda x:abs(x-myNumber))
4
请注意,它也适用于带有 int 键的字典,例如 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
来获取索引而不是值。【参考方案3】:
>>> 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 分配给名称。【参考方案4】:我将重命名函数 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
,结果如下:
所以在这个特定的测试中,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 怎么办?【参考方案5】:
def closest(list, Number):
aux = []
for valor in list:
aux.append(abs(Number-valor))
return aux.index(min(aux))
此代码将为您提供列表中最接近的 Number 的索引。
KennyTM 给出的解决方案总体上是最好的,但是在你不能使用它的情况下(比如 brython),这个功能会起作用
【讨论】:
【参考方案6】:需要注意的是,Lauritz 关于使用 bisect 的建议想法实际上并没有在 MyList 中找到最接近 MyNumber 的值。相反,bisect 在 MyList 中的 MyNumber 之后查找 order 中的下一个值。因此,在 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 的解决方案确实包含选择两个候选者中哪个更接近的代码。【参考方案7】:
扩展 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.
【讨论】:
【参考方案8】:如果我可以添加到@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
【讨论】:
【参考方案9】: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)
【讨论】:
请提供一些解释。【参考方案10】: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])
【讨论】:
以上是关于从整数列表中,获取最接近给定值的数字的主要内容,如果未能解决你的问题,请参考以下文章
c_cpp [阵] 3sum最近。给定n个整数的数组S,在S中找到三个整数,使得总和最接近给定数字target。 Retur