J'essaie de générer des points aléatoires sur la surface de la sphère à l'aide de numpy. J'ai examiné le post qui explique la distribution uniforme ici . Cependant, vous avez besoin d’idées sur la manière de générer les points uniquement à la surface de la sphère. J'ai des coordonnées (x, y, z) et le rayon de chacune de ces sphères.
Je ne suis pas très familiarisé avec les mathématiques à ce niveau et j'essaie de comprendre la simulation de Monte Carlo.
Toute aide sera très appréciée.
Merci, Parin
En vous basant sur la dernière approche de cette page , vous pouvez simplement générer un vecteur constitué d’échantillons indépendants à partir de trois distributions normales standard, puis normaliser le vecteur de sorte que sa magnitude soit égale à 1:
import numpy as np
def sample_spherical(npoints, ndim=3):
vec = np.random.randn(ndim, npoints)
vec /= np.linalg.norm(vec, axis=0)
return vec
Par exemple:
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d
phi = np.linspace(0, np.pi, 20)
theta = np.linspace(0, 2 * np.pi, 40)
x = np.outer(np.sin(theta), np.cos(phi))
y = np.outer(np.sin(theta), np.sin(phi))
z = np.outer(np.cos(theta), np.ones_like(phi))
xi, yi, zi = sample_spherical(100)
fig, ax = plt.subplots(1, 1, subplot_kw={'projection':'3d', 'aspect':'equal'})
ax.plot_wireframe(x, y, z, color='k', rstride=1, cstride=1)
ax.scatter(xi, yi, zi, s=100, c='r', zorder=10)
La même méthode généralise également le choix de points uniformément répartis sur le cercle unitaire (ndim=2
) ou sur les surfaces des hypersphères unitaires de dimension supérieure.
Les points à la surface d'une sphère peuvent être exprimés à l'aide de deux coordonnées sphériques, theta
et phi
, avec 0 < theta < 2pi
et 0 < phi < pi
.
Formule de conversion en coordonnées cartésiennes x, y, z
:
x = r * cos(theta) * sin(phi)
y = r * sin(theta) * sin(phi)
z = r * cos(phi)
où r
est le rayon de la sphère.
Ainsi, le programme pourrait échantillonner au hasard theta
et phi
dans leurs plages, à distribution uniforme, et en générer les coordonnées cartésiennes.
Mais alors les points se répartissent plus densément sur les pôles de la sphère. Pour que les points soient uniformément répartis sur la surface de la sphère, vous devez choisir phi
en tant que phi = acos(a)
, où -1 < a < 1
est choisi selon une distribution uniforme.
Pour le code Numpy, il serait identique à Échantillonnage de points aléatoires uniformément répartis à l'intérieur d'un volume sphérique , sauf que la variable radius
a une valeur fixe.
Après avoir discuté avec @Soonts, je suis curieux de connaître les performances des trois approches utilisées dans les réponses: une avec la génération d’angles aléatoires, une avec des coordonnées distribuées normalement et une avec le rejet de points uniformément répartis.
Voici ma tentative de comparaison:
import numpy as np
def sample_trig(npoints):
theta = 2*np.pi*np.random.Rand(npoints)
phi = np.arccos(2*np.random.Rand(npoints)-1)
x = np.cos(theta) * np.sin(phi)
y = np.sin(theta) * np.sin(phi)
z = np.cos(phi)
return np.array([x,y,z])
def sample_normals(npoints):
vec = np.random.randn(3, npoints)
vec /= np.linalg.norm(vec, axis=0)
return vec
def sample_reject(npoints):
vec = np.zeros((3,npoints))
abc = 2*np.random.Rand(3,npoints)-1
norms = np.linalg.norm(abc,axis=0)
mymask = norms<=1
abc = abc[:,mymask]/norms[mymask]
k = abc.shape[1]
vec[:,0:k] = abc
while k<npoints:
abc = 2*np.random.Rand(3)-1
norm = np.linalg.norm(abc)
if 1e-5 <= norm <= 1:
vec[:,k] = abc/norm
k = k+1
return vec
Puis pour 1000 points
In [449]: timeit sample_trig(1000)
1000 loops, best of 3: 236 µs per loop
In [450]: timeit sample_normals(1000)
10000 loops, best of 3: 172 µs per loop
In [451]: timeit sample_reject(1000)
100 loops, best of 3: 13.7 ms per loop
Notez que dans l'implémentation basée sur les rejets, j'ai d'abord généré des échantillons npoints
et jeté les mauvais, et j'ai uniquement utilisé une boucle pour générer le reste des points. Il semble que le rejet direct, étape par étape, dure plus longtemps. J'ai également enlevé le contrôle de division par zéro pour avoir une comparaison plus nette avec le cas sample_normals
.
Supprimer la vectorisation des deux méthodes directes les place dans le même stade:
def sample_trig_loop(npoints):
x = np.zeros(npoints)
y = np.zeros(npoints)
z = np.zeros(npoints)
for k in xrange(npoints):
theta = 2*np.pi*np.random.Rand()
phi = np.arccos(2*np.random.Rand()-1)
x[k] = np.cos(theta) * np.sin(phi)
y[k] = np.sin(theta) * np.sin(phi)
z[k] = np.cos(phi)
return np.array([x,y,z])
def sample_normals_loop(npoints):
vec = np.zeros((3,npoints))
for k in xrange(npoints):
tvec = np.random.randn(3)
vec[:,k] = tvec/np.linalg.norm(tvec)
return vec
In [464]: timeit sample_trig(1000)
1000 loops, best of 3: 236 µs per loop
In [465]: timeit sample_normals(1000)
10000 loops, best of 3: 173 µs per loop
In [466]: timeit sample_reject(1000)
100 loops, best of 3: 14 ms per loop
In [467]: timeit sample_trig_loop(1000)
100 loops, best of 3: 7.92 ms per loop
In [468]: timeit sample_normals_loop(1000)
100 loops, best of 3: 10.9 ms per loop
Selon le matériel, cela pourrait être beaucoup plus rapide.
Choisissez a, b, c
composé de trois nombres aléatoires, compris entre -1 et 1.
Calculer r2 = a^2 + b^2 + c^2
Si r2> 1.0 (= le point n'est pas dans la sphère) ou r2 <0.00001 (= le point est trop proche du centre, nous aurons une division par zéro lors de la projection sur la surface de la sphère), vous écarterez les valeurs et choisissez un autre ensemble de a, b, c
aléatoire
Sinon, vous avez votre point aléatoire (par rapport au centre de la sphère):
ir = R / sqrt(r2)
x = a * ir
y = b * ir
z = c * ir
(modifié pour refléter les corrections apportées par les commentaires)}
j'ai étudié quelques temps constants solutions à ce problème en 2004.
en supposant que vous travaillez dans des coordonnées sphériques où theta
est l’angle autour de l’axe vertical (par exemple la longitude) et phi
est l’angle élevé de l’équateur (par exemple la latitude), hémisphère nord de l’équateur vous faites ceci:
theta
Rand (0, 360).phi
= 90 * (1 - sqrt (Rand (0, 1))).pour obtenir des points sur une sphère plutôt que sur un hémisphère, il suffit alors de nier phi
50% du temps.
pour les curieux, une approche similaire est valable pour générer des points uniformément répartis sur un disque unitaire:
theta
= Rand (0, 360).radius
= sqrt (Rand (0, 1)).je n'ai aucune preuve de la justesse de ces approches, mais je les utilise avec beaucoup de succès depuis une dizaine d'années et je suis convaincu de leur exactitude.
une illustration (à partir de 2004) des différentes approches est _ = ici , notamment une visualisation de l’approche consistant à choisir des points à la surface d’un cube et à les normaliser sur la sphère.