Индекс поиска элемента, ближайшего к значению в списке, который не полностью отсортирован
В качестве примера мой список:
[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
, но они не работают.
Я не могу отсортировать этот массив, потому что индекс значения будет использоваться в аналогичном массиве для извлечения значения в этом индексе.
Ответы
Ответ 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)
Ответ 2
Как насчет: вы застегиваете два списка, а затем сортируете результат?
Ответ 3
Если вы не можете отсортировать массив, тогда нет быстрого способа найти ближайший элемент - вам нужно перебирать все записи.
Существует обходное решение, но это довольно много работы: Напишите алгоритм сортировки, который сортирует массив и (в то же время) обновляет второй массив, который сообщает вам, где эта запись была до того, как массив был отсортирован.
Таким образом, вы можете использовать бинарный поиск для поиска индекса ближайшей записи, а затем использовать этот индекс для поиска исходного индекса, используя "индексный массив".
[EDIT] Используя zip()
, это довольно просто:
array_to_sort = zip( original_array, range(len(original_array)) )
array_to_sort.sort( key=i:i[0] )
Теперь вы можете выполнить двоичный поиск значения (используя item[0]
). item[1]
предоставит вам исходный индекс.
Ответ 4
Переход через все элементы только линейный. Если вы отсортируете массив, который будет хуже.
Я не вижу проблемы с сохранением дополнительного deltax
(разница мин до сих пор) и idx
(индекс этого элемента) и просто цикл через список.
Ответ 5
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 уже является массивом, соответствующее преобразование может быть опущено.
Ответ 6
Имейте в виду, что если пространство не важно, вы можете сортировать любой список, не перемещая содержимое, создавая дополнительный список отсортированных индексов.
Также имейте в виду, что если вы делаете это, посмотрите только один раз, вам просто нужно будет пересечь каждый элемент в списке O (n). (Если несколько раз, то вы, вероятно, захотите сортировать для повышения эффективности позже)
Ответ 7
Если вы много раз выполняете поиск в длинном списке, то min
очень плохо масштабируется (O (n ^ 2), я думаю, если вы добавите некоторые из своих запросов в список поиска).
Бисект твой друг. Здесь мое решение. Масштабируется 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