Je veux générer un groupe (x, y) de coordonnées de 0 à 2500 qui exclut les points qui sont à moins de 200 les uns des autres sans récursivité.
En ce moment, je le fais vérifier à travers une liste de toutes les valeurs précédentes pour voir si certaines sont suffisamment éloignées de toutes les autres. C'est vraiment inefficace et si j'ai besoin de générer un grand nombre de points, cela prend une éternité.
Alors, comment pourrais-je faire cela?
Il s'agit d'une variante de la suggestion de Hank Ditton qui devrait être plus efficace en termes de temps et de mémoire, surtout si vous sélectionnez relativement peu de points sur tous les points possibles. L'idée est que, chaque fois qu'un nouveau point est généré, tout ce qui se trouve à moins de 200 unités est ajouté à un ensemble de points à exclure, par rapport auquel tous les points fraîchement générés sont vérifiés.
import random
radius = 200
rangeX = (0, 2500)
rangeY = (0, 2500)
qty = 100 # or however many points you want
# Generate a set of all points within 200 of the Origin, to be used as offsets later
# There's probably a more efficient way to do this.
deltas = set()
for x in range(-radius, radius+1):
for y in range(-radius, radius+1):
if x*x + y*y <= radius*radius:
deltas.add((x,y))
randPoints = []
excluded = set()
i = 0
while i<qty:
x = random.randrange(*rangeX)
y = random.randrange(*rangeY)
if (x,y) in excluded: continue
randPoints.append((x,y))
i += 1
excluded.update((x+dx, y+dy) for (dx,dy) in deltas)
print randPoints
Je généraliserais les points, target_N < input_N
, et filtrez-les à l'aide d'un KDTree . Par exemple:
import numpy as np
from scipy.spatial import KDTree
N = 20
pts = 2500*np.random.random((N,2))
tree = KDTree(pts)
print tree.sparse_distance_matrix(tree, 200)
Me donnerait des points qui sont "proches" les uns des autres. À partir d'ici, il devrait être simple d'appliquer n'importe quel filtre:
(11, 0) 60.843426339
(0, 11) 60.843426339
(1, 3) 177.853472309
(3, 1) 177.853472309
Quelques options:
Cela a été répondu, mais c'est très tangentiellement lié à mon travail, alors j'ai essayé. J'ai implémenté l'algorithme décrit dans cette note que j'ai trouvé lié depuis ce billet de blog . Malheureusement, ce n'est pas plus rapide que les autres méthodes proposées, mais je suis sûr qu'il y a des optimisations à faire.
import numpy as np
import matplotlib.pyplot as plt
def lonely(p,X,r):
m = X.shape[1]
x0,y0 = p
x = y = np.arange(-r,r)
x = x + x0
y = y + y0
u,v = np.meshgrid(x,y)
u[u < 0] = 0
u[u >= m] = m-1
v[v < 0] = 0
v[v >= m] = m-1
return not np.any(X[u[:],v[:]] > 0)
def generate_samples(m=2500,r=200,k=30):
# m = extent of sample domain
# r = minimum distance between points
# k = samples before rejection
active_list = []
# step 0 - initialize n-d background grid
X = np.ones((m,m))*-1
# step 1 - select initial sample
x0,y0 = np.random.randint(0,m), np.random.randint(0,m)
active_list.append((x0,y0))
X[active_list[0]] = 1
# step 2 - iterate over active list
while active_list:
i = np.random.randint(0,len(active_list))
rad = np.random.Rand(k)*r+r
theta = np.random.Rand(k)*2*np.pi
# get a list of random candidates within [r,2r] from the active point
candidates = np.round((rad*np.cos(theta)+active_list[i][0], rad*np.sin(theta)+active_list[i][1])).astype(np.int32).T
# trim the list based on boundaries of the array
candidates = [(x,y) for x,y in candidates if x >= 0 and y >= 0 and x < m and y < m]
for p in candidates:
if X[p] < 0 and lonely(p,X,r):
X[p] = 1
active_list.append(p)
break
else:
del active_list[i]
return X
X = generate_samples(2500, 200, 10)
s = np.where(X>0)
plt.plot(s[0],s[1],'.')
Et les résultats:
Par le lien, la méthode d'aganders3 est connue sous le nom d'échantillonnage de disque de Poisson. Vous pourrez peut-être trouver des implémentations plus efficaces qui utilisent une recherche de grille locale pour trouver des "chevauchements". Par exemple échantillonnage du disque de Poisson . Parce que vous contraignez le système, il ne peut pas être complètement aléatoire. L'emballage maximum pour les cercles avec des rayons uniformes dans un plan est de ~ 90% et est atteint lorsque les cercles sont disposés dans un réseau hexagonal parfait. À mesure que le nombre de points que vous demandez approche de la limite théorique, l'agencement généré devient plus hexagonal. D'après mon expérience, il est difficile d'obtenir un emballage supérieur à ~ 60% avec des cercles uniformes en utilisant cette approche.