查找最接近未完全排序的列表中的值的项目的索引

Posted

技术标签:

【中文标题】查找最接近未完全排序的列表中的值的项目的索引【英文标题】:Finding index of an item closest to the value in a list that's not entirely sorted 【发布时间】:2012-03-31 03:40:17 【问题描述】:

例如,我的清单是:

[25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866,
 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154,
 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152,
 6.983734, 6.048035, 5.505096, 4.65799]

我正在寻找最接近11.5 的值的索引。我尝试过其他方法,例如二分查找和bisect_left,但它们不起作用。

我无法对这个数组进行排序,因为值的索引将用于类似的数组以获取该索引处的值。

【问题讨论】:

from list of integers, get number closest to a given value的可能重复 @Jean-FrançoisCorbett 这个问题怎么可能是另一个问题的重复。这个问题比较老。 @QiuYU 因为this 【参考方案1】:

尝试以下方法:

min(range(len(a)), key=lambda i: abs(a[i]-11.5))

例如:

>>> a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
>>> min(range(len(a)), key=lambda i: abs(a[i]-11.5))
16

或者获取索引和值:

>>> min(enumerate(a), key=lambda x: abs(x[1]-11.5))
(16, 11.33447)

【讨论】:

def find_nearest(array, value): array = np.asarray(array); idx = (np.abs(array - value)).argmin(); return idx; 执行得更快(灵感来自***.com/a/2566508/1273751) @HomeroEsmeraldo 您的功能运行良好,非常感谢。【参考方案2】:
import numpy as np

a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]

index = np.argmin(np.abs(np.array(a)-11.5))
a[index] # here is your result

如果a已经是一个数组,可以省略对应的变换。

【讨论】:

【参考方案3】:

怎么样:你压缩两个列表,然后对结果进行排序?

【讨论】:

【参考方案4】:

如果您无法对数组进行排序,那么就没有快速找到最接近的项目的方法 - 您必须遍历所有条目。

有一个解决方法,但工作量很大:编写一个排序算法,对数组进行排序并(同时)更新第二个数组,告诉你这个条目在 之前数组已排序。

这样,您可以使用二进制搜索查找最近条目的索引,然后使用该索引使用“索引数组”查找原始索引。

[编辑]使用zip(),这很容易实现:

 array_to_sort = zip( original_array, range(len(original_array)) )
 array_to_sort.sort( key=i:i[0] )

现在您可以对值进行二分搜索(使用item[0])。 item[1] 会给你原始索引。

【讨论】:

【参考方案5】:

遍历所有项目只是线性的。如果你对数组进行排序会更糟。

我认为保留额外的deltax(到目前为止的最小差异)和idx(该元素的索引)并在列表中循环一次没有问题。

【讨论】:

【参考方案6】:

请记住,如果空间不重要,您可以通过创建已排序索引的辅助列表来对任何列表进行排序而无需移动内容。

还请记住,如果您只进行一次查找,那么您只需遍历列表中的每个元素 O(n)。 (如果多次,那么您可能希望稍后进行排序以提高效率)

【讨论】:

【参考方案7】:

如果您多次搜索长列表,那么 min 的扩展性非常差(O(n^2),如果您将一些搜索附加到搜索列表,我想)。

Bisect 是你的朋友。这是我的解决方案。它缩放 O(n*log(n)):

class Closest:
    """Assumes *no* redundant entries - all inputs must be unique"""
    def __init__(self, numlist=None, firstdistance=0):
        if numlist == None:
            numlist=[]
        self.numindexes = dict((val, n) for n, val in enumerate(numlist))
        self.nums = sorted(self.numindexes)
        self.firstdistance = firstdistance

    def append(self, num):
        if num in self.numindexes:
            raise ValueError("Cannot append '%s' it is already used" % str(num))
        self.numindexes[num] = len(self.nums)
        bisect.insort(self.nums, num)

    def rank(self, target):
        rank = bisect.bisect(self.nums, target)
        if rank == 0:
            pass
        elif len(self.nums) == rank:
            rank -= 1
        else:
            dist1 = target - self.nums[rank - 1]
            dist2 = self.nums[rank] - target
            if dist1 < dist2:
                rank -= 1
        return rank

    def closest(self, target):
        try:
            return self.numindexes[self.nums[self.rank(target)]]
        except IndexError:
            return 0

    def distance(self, target):
        rank = self.rank(target)
        try:
            dist = abs(self.nums[rank] - target)
        except IndexError:
            dist = self.firstdistance
        return dist

像这样使用它:

a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866,
     19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154,
     13.09409, 12.18347, 1.33447, 10.32184, 9.544922, 8.813385, 8.181152,
     6.983734, 6.048035, 5.505096, 4.65799]
targets = [1.0, 100.0, 15.0, 15.6, 8.0]
cl = Closest(a)
for x in targets:
    rank = cl.rank(x)
    print("Closest to %5.1f : rank=%2i num=%8.5f index=%2i " % (x, rank,
        cl.nums[rank], cl.closest(x)))

将输出:

Closest to   1.0 : rank= 0 num= 1.33447 index=16
Closest to 100.0 : rank=25 num=26.78030 index= 1
Closest to  15.0 : rank=12 num=14.79059 index=12
Closest to  15.6 : rank=13 num=15.71255 index=11
Closest to   8.0 : rank= 5 num= 8.18115 index=20

还有:

cl.append(99.9)
x = 100.0
rank = cl.rank(x)
print("Closest to %5.1f : rank=%2i num=%8.5f index=%2i " % (x, rank,
    cl.nums[rank], cl.closest(x)))

输出:

Closest to 100.0 : rank=25 num=99.90000 index=25

【讨论】:

或者干脆使用cl.closest(target),对吧?感谢您的代码! 是的,完全正确!很高兴你喜欢它 It's impossible to find the minimum of an unsorted list in O(log n)。由于我们无法知道列表的哪些子列表已排序,因此二等分无济于事,我们必须触及每个元素。 @Jan 我认为你是对的我会修改措辞

以上是关于查找最接近未完全排序的列表中的值的项目的索引的主要内容,如果未能解决你的问题,请参考以下文章

查找功能不适用于未排序的列表

python 选择排序

在排序数组列表中查找2个最接近的先前值和2个最接近的下一个值

在 Python 中,如何找到排序列表中第一个大于阈值的值的索引?

ghpython_选择排序

排序加二分查找解题