from list of integers, get number closest to a given value
给定一个整数列表,我想找出哪个数字最接近我在输入中给出的数字:
1 2 3 4 5 | >>> myList = [4, 1, 88, 44, 3] >>> myNumber = 5 >>> takeClosest(myList, myNumber) ... 4 |
有什么快速的方法吗?
如果我们不确定列表是否排序,可以使用内置的
1 2 | >>> min(myList, key=lambda x:abs(x-myNumber)) 4 |
号
注意,它还可以与带int键的dict一起使用,比如
如果列表已经排序,或者您只需支付一次数组排序的费用,请使用@lauritz's answer中所示的平分方法,该方法只需要O(log n)时间(注意,检查列表是否已经排序为O(n),排序是否为O(n logn)。
如果您的意思是"快速执行"而不是"快速写入",那么
"几乎"来自这样一个事实,即
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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. """ 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)必须位于哪个部分中,从而进行对分。这意味着它的运行时间为O(log n),而不是最高投票答案的运行时间。如果我们比较这两种方法,并将它们与排序后的
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ python -m timeit -s" from closest import takeClosest from random import randint a = range(-1000, 1000, 10)""takeClosest(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 |
。
所以在这个特定的测试中,
如果我们取消了必须对
这是一个奇怪的结果,考虑到排序步骤是O(n log(n))!
1 2 3 | >>> takeClosest = lambda num,collection:min(collection,key=lambda x:abs(x-num)) >>> takeClosest(5,[4,1,88,44,3]) 4 |
lambda是编写"匿名"函数(一个没有名称的函数)的特殊方法。因为lambda是一个表达式,所以可以给它指定任何名称。
写上述内容的"长"方法是:
1 2 | def takeClosest(num,collection): return min(collection,key=lambda x:abs(x-num)) |
。
1 2 3 4 5 6 | def closest(list, Number): aux = [] for valor in list: aux.append(abs(Number-valor)) return aux.index(min(aux)) |
。
此代码将为您提供列表中最接近数字的索引。
KennyTM给出的解决方案总体上是最好的,但是在您不能使用它的情况下(如Bryton),这个函数将完成这项工作。
遍历列表并将当前最接近的数字与
1 2 3 4 5 6 | def takeClosest(myList, myNumber): closest = myList[0] for i in range(1, len(myList)): if abs(i - myNumber) < closest: closest = i return closest |
重要的是要注意,劳里茨使用平分的建议思想实际上并没有在mylist中找到与mynumber最接近的值。相反,二分法在mylist中按mynumber之后的顺序查找下一个值。所以在OP的例子中,你会得到44的位置,而不是4的位置。
1 2 3 4 5 6 | >>> myList = [1, 3, 4, 44, 88] >>> myNumber = 5 >>> pos = (bisect_left(myList, myNumber)) >>> myList[pos] ... 44 |
要获得最接近5的值,可以尝试将列表转换为数组,并使用类似numpy的argmin。
1 2 3 4 5 6 7 8 | >>> 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的答案
为了避免出现运行错误别忘了在
1 2 | if (myNumber > myList[-1] or myNumber < myList[0]): return False |
号
所以完整的代码看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 |
号
扩展古斯塔沃·利马的回答。同样的事情也可以在不创建一个全新列表的情况下完成。随着
1 2 3 4 5 | 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)) |
号
1 2 3 | 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. |
。