Voici une technique assez simple utilisant Python simple qui peut réduire le nombre de comparaisons nécessaires.
Nous commençons par trier les points le long de l'axe X, Y ou Z (sélectionné par axis
dans le code ci-dessous). Disons que nous choisissons l'axe X. Ensuite, nous bouclons sur des paires de points comme le fait votre code, mais lorsque nous trouvons une paire dont la distance est supérieure à masking_radius
, nous vérifions si la différence de leurs coordonnées X est également supérieure à masking_radius
. Si c'est le cas, alors nous pouvons sortir de la boucle interne j
car tous les points avec un j
plus grand ont une plus grande coordonnée X. La fonction dist2
calcule la distance au carré. C'est plus rapide que de calculer la distance réelle car le calcul de la racine carrée est relativement lent.
J'ai également inclus un code qui se comporte comme votre code, c'est-à-dire, il teste chaque paire de points, à des fins de comparaison de vitesse; il sert également à vérifier que le code rapide est correct.;)
from random import seed, uniform
from operator import itemgetter
seed(42)
# Make some fake data
def make_point(hi=10.0):
return [uniform(-hi, hi) for _ in range(3)]
psize = 1000
points = [make_point() for _ in range(psize)]
masking_radius = 4.0
masking_radius2 = masking_radius ** 2
def dist2(p, q):
return (p[0] - q[0])**2 + (p[1] - q[1])**2 + (p[2] - q[2])**2
pair_count = 0
test_count = 0
do_fast = 1
if do_fast:
# Sort the points on one axis
axis = 0
points.sort(key=itemgetter(axis))
# Fast
for i, p in enumerate(points):
left, right = i - 1, i + 1
for j in range(i + 1, psize):
test_count += 1
q = points[j]
if dist2(p, q) < masking_radius2:
#interaction_pairs.append((i, j))
pair_count += 1
elif q[axis] - p[axis] >= masking_radius:
break
if i % 100 == 0:
print('\r {:3} '.format(i), flush=True, end='')
total_pairs = psize * (psize - 1) // 2
print('\r {}/{} tests'.format(test_count, total_pairs))
else:
# Slow
for i, p in enumerate(points):
for j in range(i+1, psize):
q = points[j]
if dist2(p, q) < masking_radius2:
#interaction_pairs.append((i, j))
pair_count += 1
if i % 100 == 0:
print('\r {:3} '.format(i), flush=True, end='')
print('\n', pair_count, 'pairs')
sortie avec do_fast = 1
181937/499500 tests
13295 pairs
sortie avec do_fast = 0
13295 pairs
Bien sûr, si la plupart des paires de points sont à masking_radius
les uns des autres, il y a ne sera pas beaucoup d'avantages à utiliser cette technologie ique. Et le tri des points ajoute un peu de temps, mais TimSort de Python est plutôt efficace, surtout si les données sont déjà partiellement triées, donc si le masking_radius
est suffisamment petit, vous devriez voir une amélioration notable de la vitesse.
Les points sont-ils en 2 ou 3 dimensions? Si 2, vous pouvez essayer une variante de l'algorithme «Trouver la paire de points la plus proche» donné dans Introduction aux algorithmes de Cormen et al. –
Votre algorithme a une complexité temporelle 'O (n ** 2)' alors que Cormen a 'O (n * log (n))'. Vous pouvez aussi essayer [pypy] (http://pypy.org/) qui accélère beaucoup le code Python. –