Вы можете использовать бинарный поиск, чтобы сделать поиск намного быстрее.
Бинарный поиск делает поиск O (log (n)), а не O (n) (используя индекс)
Нам не нужно сортировать кортежи, поскольку они уже отсортированы генератором
import bisect
def get_tuples(length, total):
" Generates tuples "
if length == 1:
yield (total,)
return
yield from ((i,) + t for i in range(total + 1) for t in get_tuples(length - 1, total - i))
def find_indexes(x, indexes):
if len(indexes) > 100:
# Faster to generate all indexes when we have a large
# number to check
d = dict(zip(x, range(len(x))))
return [d[tuple(i)] for i in indexes]
else:
return [bisect.bisect_left(x, tuple(i)) for i in indexes]
# Generate tuples (in this case 4, 20)
x = list(get_tuples(4, 20))
# Tuples are generated in sorted order [(0,0,0,20), ...(20,0,0,0)]
# which allows binary search to be used
indexes = [[ 0, 0, 0, 20],
[ 0, 0, 7, 13],
[ 0, 5, 5, 10],
[ 0, 0, 5, 15],
[ 0, 2, 4, 14]]
y = find_indexes(x, indexes)
print('Found indexes:', *y)
print('Indexes & Tuples:')
for i in y:
print(i, x[i])
Выход
Found indexes: 0 7 100 5 45
Indexes & Tuples:
0 (0, 0, 0, 20)
7 (0, 0, 7, 13)
100 (0, 5, 5, 10)
5 (0, 0, 5, 15)
45 (0, 2, 4, 14)
Производительность
Сценарий 1 - Кортежи уже вычислены, и мы просто хотим найти индекс определенных кортежей
Например, x = list (get_tuples (4, 20)) уже выполнен.
Поиск
indexes = [[ 0, 0, 0, 20],
[ 0, 0, 7, 13],
[ 0, 5, 5, 10],
[ 0, 0, 5, 15],
[ 0, 2, 4, 14]]
Двоичный поиск
%timeit find_indexes(x, indexes)
100000 loops, best of 3: 11.2 µs per loop
Вычисляет индекс на основе одного кортежа (любезность @PaulPanzer подход)
%timeit get_idx(indexes)
10000 loops, best of 3: 92.7 µs per loop
В этом сценариидвоичный поиск выполняется в ~ 8 раз быстрее, если кортежи уже были предварительно вычислены.
Сценарий 2 - кортежи не были предварительно вычислены.
%%timeit
import bisect
def find_indexes(x, t):
" finds the index of each tuple in list t (assumes x is sorted) "
return [bisect.bisect_left(x, tuple(i)) for i in t]
# Generate tuples (in this case 4, 20)
x = list(get_tuples(4, 20))
indexes = [[ 0, 0, 0, 20],
[ 0, 0, 7, 13],
[ 0, 5, 5, 10],
[ 0, 0, 5, 15],
[ 0, 2, 4, 14]]
y = find_indexes(x, indexes)
100 loops, best of 3: 2.69 ms per loop
Подход @PaulPanzer в этом сценарии совпадает по времени (92,97 мкс)
=> Подход @PaulPanzer ~ в 29 раз быстрее, когда кортежи ненеобходимо вычислить
Сценарий 3 - Большое количество индексов (@PJORR) Сгенерировано большое количество случайных индексов
x = list(get_tuples(4, 20))
xnp = np.array(x)
indices = xnp[np.random.randint(0,len(xnp), 2000)]
indexes = indices.tolist()
%timeit find_indexes(x, indexes)
#Result: 1000 loops, best of 3: 1.1 ms per loop
%timeit get_idx(indices)
#Result: 1000 loops, best of 3: 716 µs per loop
В этом случаемы @PaulPanzer на 53% быстрее