Из вашего первого поста было неясно, одинаково ли количество координат в структурах, поэтому я предположил, что это не так.
Вот немного пересмотренная версия вашего наивного подхода и первая улучшенная версия использование быстрой низкоуровневой векторизации NumPy.
import numpy as np
def naive(data):
res = np.inf
for k1, v1 in data.items():
for k2, v2 in data.items():
if k1 == k2:
continue
for c1 in v1:
for c2 in v2:
res = np.minimum(res, np.sum((c1 - c2)**2))
return np.sqrt(res)
def version1(data):
res = np.inf
for k1, v1 in data.items():
for k2, v2 in data.items():
if k1 == k2:
continue
res = np.minimum(res, np.min(np.sum((v1[None, ...] - v2[:, None, :])**2, axis=-1)))
return np.sqrt(res)
. Критическая точка - v1[None, ...] - v2[:, None, :]
, где, добавляя дополнительную ось к каждой структуре в другом месте, мы используем вещание NumPy чтобы удалить две внутренние петли.
Тестирование ваших данных (требуется I Python, просто чтобы использовать упрощенный интерфейс для timeit
):
struc_dict = {
'struc1' : [[-31.447, -4.428, -28.285], [-32.558, -2.108, -29.213], [-31.656, -4.071, -30.89 ], [-33.899, -4.504, -29.349]],
'struc2' : [[-27.487, -15.05, -31.418], [-29.178, -14.63, -33.498], [-29.548, -16.754, -31.937], [-30.028, -14.278, -30.977]],
'struc3' : [[-16.07, -2.042, -29.853], [-16.734, -4.162, -29.905], [-16.279, -4.438, -28.936], [-16.544, -4.098, -31.514]]}
data = {k: np.array(v) for k,v in struc_dict.items()}
%timeit naive(data)
%timeit version1(data)
Вывод:
1000 loops, best of 3: 433 µs per loop
10000 loops, best of 3: 55.7 µs per loop
Чтобы лучше оценить производительность, давайте попробуем получить больше данных:
np.random.seed(42)
for n in [10, 20, 50, 100]:
for max_size in [10, 20, 50, 100]:
data = {str(i): np.random.normal(size=[np.random.randint(1, max_size), 3])
for i in range(n)}
print("Measuring for n=%r and max_size=%r" % (n, max_size))
%timeit naive(data)
%timeit version1(data)
Вывод:
Measuring for n=10 and max_size=10
100 loops, best of 3: 10.6 ms per loop
1000 loops, best of 3: 784 µs per loop
Measuring for n=10 and max_size=20
10 loops, best of 3: 32.5 ms per loop
1000 loops, best of 3: 894 µs per loop
Measuring for n=10 and max_size=50
1 loop, best of 3: 323 ms per loop
1000 loops, best of 3: 1.94 ms per loop
Measuring for n=10 and max_size=100
1 loop, best of 3: 478 ms per loop
100 loops, best of 3: 2.36 ms per loop
Measuring for n=20 and max_size=10
10 loops, best of 3: 43.3 ms per loop
100 loops, best of 3: 3.32 ms per loop
Measuring for n=20 and max_size=20
10 loops, best of 3: 188 ms per loop
100 loops, best of 3: 3.78 ms per loop
Measuring for n=20 and max_size=50
1 loop, best of 3: 1.41 s per loop
100 loops, best of 3: 8.09 ms per loop
Measuring for n=20 and max_size=100
1 loop, best of 3: 4.34 s per loop
100 loops, best of 3: 17.8 ms per loop
Measuring for n=50 and max_size=10
1 loop, best of 3: 341 ms per loop
10 loops, best of 3: 22.6 ms per loop
Measuring for n=50 and max_size=20
1 loop, best of 3: 1.24 s per loop
10 loops, best of 3: 24.3 ms per loop
Measuring for n=50 and max_size=50
1 loop, best of 3: 7.86 s per loop
10 loops, best of 3: 45.9 ms per loop
Measuring for n=50 and max_size=100
1 loop, best of 3: 22.6 s per loop
10 loops, best of 3: 97.1 ms per loop
Measuring for n=100 and max_size=10
1 loop, best of 3: 1.03 s per loop
10 loops, best of 3: 83.5 ms per loop
Measuring for n=100 and max_size=20
1 loop, best of 3: 4 s per loop
10 loops, best of 3: 96.1 ms per loop
Measuring for n=100 and max_size=50
1 loop, best of 3: 27.4 s per loop
10 loops, best of 3: 180 ms per loop
Measuring for n=100 and max_size=100
1 loop, best of 3: 1min 50s per loop
1 loop, best of 3: 447 ms per loop
Ускорение сильно зависит от количества координат ( чем больше, тем лучше, если на вашей машине достаточно свободной оперативной памяти).
Дальнейших улучшений можно добиться с помощью:
- Использование
np.einsum
(суммирование Эйнштейна) вместо np.sum
, который хорошо известен т o быть быстрее - Если все структуры имеют одинаковое количество координат, вы можете удалить два других цикла, построив два четырехмерных массива (например, Nx1xMx3 и 1xNxMx3, где N - это число структур, а M - это число количество координат для каждого).
- Объедините обе предыдущие точки!