「スキャンライン」手法を使用して、線形時間( )からすべての間隔にlist2
ある整数を見つけることができます(ネストされた間隔をカウントするためのすべての重複する間隔とサブO(n ^ 2)アルゴリズムを見つける方法を参照してください)。[x - within, x + within]
x
list1
O(n)
対応する間隔を列挙するには、時間がlist1
必要です。ここで、は間隔の数です。つまり、全体的なアルゴリズムは次のとおりです。O(m)
m
O(n*m)
from collections import namedtuple
from heapq import merge
def find_items_within(list1, list2, within):
issorted = lambda L: all(x <= y for x, y in zip(L, L[1:]))
assert issorted(list1) and issorted(list2) and within >= 0
# get sorted endpoints - O(n) (due to list1, list2 are sorted)
Event = namedtuple('Event', "endpoint x type")
def get_events(lst, delta, type):
return (Event(x + delta, x, type) for x in lst)
START, POINT, END = 0, 1, 2
events = merge(get_events(list1, delta=-within, type=START),
get_events(list1, delta=within, type=END),
get_events(list2, delta=0, type=POINT))
# O(n * m), m - number of points in `list1` that are
# within distance from given point in `list2`
started = set() # started intervals
for e in events: # O(n)
if e.type is START: # started interval
started.add(e.x) # O(m) is worst case (O(1) amortized)
elif e.type is END: # ended interval
started.remove(e.x) # O(m) is worst case (O(1) amortized)
else: # found point
assert e.type is POINT
for x in started: # O(m)
yield x, e.x
で重複する値を許可するにはlist1
; x
にそれぞれのインデックスを追加し、セットの代わりにEvent
辞書を使用することができます。index -> x
started