web-dev-qa-db-fra.com

Générer un échantillon aléatoire de points répartis sur la surface d'une unité de sphère

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

12
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)

 enter image description here

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.

22
ali_m

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)

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.

7
tmlen

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
3
Andras Deak

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
2
Soonts

(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:

  1. choisissez thetaRand (0, 360).
  2. choisissez 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:

  1. choisissez theta = Rand (0, 360).
  2. choisissez 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.

0
orion elenzil