I am looking to be able to generate a random uniform sample of particle locations that fall within a spherical volume.
The image below (courtesy of http://nojhan.fre
There is a brilliant way to generate uniformly points on sphere in n-dimensional space, and you have pointed this in your question (I mean MATLAB code).
Why does it work? The answer is: let us look at the probability density of n-dimensional normal distribution. It is equal (up to constant)
exp(-x_1*x_1/2) *exp(-x_2*x_2/2)... = exp(-r*r/2), so it doesn't depend on the direction, only on the distance! This means, after you normalize vector, the resulting distribution's density will be constant across the sphere.
This method should be definitely preferred due to it's simplicity, generality and efficiency (and beauty). The code, which generates 1000 events on the sphere in three dimensions:
size = 1000
n = 3 # or any positive integer
x = numpy.random.normal(size=(size, n))
x /= numpy.linalg.norm(x, axis=1)[:, numpy.newaxis]
BTW, the good link to look at: http://www-alg.ist.hokudai.ac.jp/~jan/randsphere.pdf
As for having uniform distribution within a sphere, instead of normalizing a vector, you should multiply vercor by some f(r): f(r)*r is distributed with density proportional to r^n on [0,1], which was done in the code you posted
I agree with Alleo. I translated your Matlab code to Python and it can generate thousands of points very fast (a fraction of second in my computer for 2D and 3D). I've even ran it for up to 5D hyperspheres. I found your code so useful that I'm applying it in a study. Tim McJilton, who should I add as reference?
import numpy as np
from scipy.special import gammainc
from matplotlib import pyplot as plt
def sample(center,radius,n_per_sphere):
r = radius
ndim = center.size
x = np.random.normal(size=(n_per_sphere, ndim))
ssq = np.sum(x**2,axis=1)
fr = r*gammainc(ndim/2,ssq/2)**(1/ndim)/np.sqrt(ssq)
frtiled = np.tile(fr.reshape(n_per_sphere,1),(1,ndim))
p = center + np.multiply(x,frtiled)
return p
fig1 = plt.figure(1)
ax1 = fig1.gca()
center = np.array([0,0])
radius = 1
p = sample(center,radius,10000)
ax1.scatter(p[:,0],p[:,1],s=0.5)
ax1.add_artist(plt.Circle(center,radius,fill=False,color='0.5'))
ax1.set_xlim(-1.5,1.5)
ax1.set_ylim(-1.5,1.5)
ax1.set_aspect('equal')
import numpy as np
import matplotlib.pyplot as plt
r= 30.*np.sqrt(np.random.rand(1000))
#r= 30.*np.random.rand(1000)
phi = 2. * np.pi * np.random.rand(1000)
x = r * np.cos(phi)
y = r * np.sin(phi)
plt.figure()
plt.plot(x,y,'.')
plt.show()
this is what you want