例として私のリストは次のとおりです。
[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
などの他の方法を試しましたが、機能しません。
値のインデックスは、そのインデックスで値を取得するために同様の配列で使用されるため、この配列を並べ替えることはできません。
以下を試してください:
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つのリストを圧縮して、結果を並べ替えますか?
配列を並べ替えることができない場合、最も近いアイテムをすばやく見つける方法はありません。すべてのエントリを反復処理する必要があります。
回避策はありますが、かなり手間がかかります。配列をソートするソートアルゴリズムを記述し、(同時に)2番目の配列を更新して、このエントリがbefore配列がソートされました。
このように、バイナリ検索を使用して最も近いエントリのインデックスを検索し、このインデックスを使用して「インデックス配列」を使用して元のインデックスを検索できます。
[EDIT]Zip()
を使用すると、これは簡単に実現できます。
array_to_sort = Zip( original_array, range(len(original_array)) )
array_to_sort.sort( key=i:i[0] )
これで、値をバイナリ検索できます(item[0]
)。 item[1]
は元のインデックスを提供します。
すべてのアイテムをたどるのは線形にすぎません。配列を並べ替えると、さらに悪化します。
追加のdeltax
(これまでの最小差)とidx
(その要素のインデックス)を保持して、リストを1回ループするだけで問題が発生することはありません。
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が既に配列の場合、対応する変換は省略できます。
スペースが重要でない場合は、ソートされたインデックスのセカンダリリストを作成して、コンテンツを移動せずにリストをソートできることに注意してください。
また、このルックアップを1回だけ行う場合は、リストO(n)内のすべての要素をトラバースするだけでよいことに注意してください。 (複数回の場合、後で効率を上げるためにソートすることをお勧めします)
長いリストを何度も検索する場合、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 '%i' it is already used" % 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, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
cl = Closest(a)
for x in targets:
rank = cl.rank(x)
print("Closest number:", cl.nums[rank])
print("Closest index:", cl.numindexes[cl.nums[rank]])