Сэмплирование равномерно распределенных случайных точек внутри сферического объема

Я ищу, чтобы иметь возможность генерировать случайный однородный образец местоположений частиц, которые попадают в сферический объем.

Изображение ниже (любезно предоставлено http://nojhan.free.fr/metah/) показывает, что я ищу. Это срез через сферу, показывающий равномерное распределение точек:

Uniformly distributed circle

Это то, что я сейчас получаю:

Uniformly Distributed but Cluster Of Points

Вы можете видеть, что в центре есть кластер точек из-за преобразования между сферическими и декартовыми координатами.

Используемый мной код:

def new_positions_spherical_coordinates(self):
   radius = numpy.random.uniform(0.0,1.0, (self.number_of_particles,1)) 
   theta = numpy.random.uniform(0.,1.,(self.number_of_particles,1))*pi
   phi = numpy.arccos(1-2*numpy.random.uniform(0.0,1.,(self.number_of_particles,1)))
   x = radius * numpy.sin( theta ) * numpy.cos( phi )
   y = radius * numpy.sin( theta ) * numpy.sin( phi )
   z = radius * numpy.cos( theta )
   return (x,y,z)

Ниже приведен некоторый код MATLAB, который предположительно создает однородный сферический образец, который аналогичен уравнению, указанному http://nojhan.free.fr/metah. Я просто не могу его расшифровать или понять, что они сделали.

function X = randsphere(m,n,r)

% This function returns an m by n array, X, in which 
% each of the m rows has the n Cartesian coordinates 
% of a random point uniformly-distributed over the 
% interior of an n-dimensional hypersphere with 
% radius r and center at the origin.  The function 
% 'randn' is initially used to generate m sets of n 
% random variables with independent multivariate 
% normal distribution, with mean 0 and variance 1.
% Then the incomplete gamma function, 'gammainc', 
% is used to map these points radially to fit in the 
% hypersphere of finite radius r with a uniform % spatial distribution.
% Roger Stafford - 12/23/05

X = randn(m,n);
s2 = sum(X.^2,2);
X = X.*repmat(r*(gammainc(s2/2,n/2).^(1/n))./sqrt(s2),1,n);

Я был бы очень признателен за любые предложения по созданию действительно однородного образца из сферического тома в Python.

Кажется, есть много примеров, показывающих, как сэмплировать из однородной сферической оболочки, но это проще, чем проще. Проблема связана с масштабированием - должно быть меньше частиц в радиусе 0,1, чем в радиусе 1,0, чтобы создать однородный образец из объема сферы.

Изменить: Исправлено и устранено тот факт, который я попросил, и я имел в виду униформу.

Ответ 1

В то время как я предпочитаю метод отбрасывания сфер, для полноты Я предлагаю точное решение.

В сферических координатах, используя правило выборки:

phi = random(0,2pi)
costheta = random(-1,1)
u = random(0,1)

theta = arccos( costheta )
r = R * cuberoot( u )

теперь у вас есть группа (r, theta, phi), которая может быть преобразована в (x, y, z) обычным способом

x = r * sin( theta) * cos( phi )
y = r * sin( theta) * sin( phi )
z = r * cos( theta )

Ответ 2

Создайте множество точек, равномерно распределенных внутри куба, затем отбросьте те, расстояние от центра которых превышает радиус искомой сферы.

Ответ 3

Существует блестящий способ создания равномерно точек на сфере в n-мерном пространстве, и вы указали это в своем вопросе (я имею в виду код MATLAB).

Почему это работает? Ответ: рассмотрим плотность вероятности n-мерного нормального распределения. Он равен (до постоянной)

exp (-x_1 * x_1/2) * exp (-x_2 * x_2/2)... = exp (-r * r/2), поэтому он не зависит от направления, только на расстоянии! Это означает, что после нормализации вектора результирующая плотность распределения будет постоянной по всей сфере.

Этот метод должен быть определенно предпочтительным из-за его простоты, общности и эффективности (и красоты). Код, который генерирует 1000 событий на сфере в трех измерениях:

size = 1000
n = 3 # or any positive integer
x = numpy.random.normal(size=(size, n)) 
x /= numpy.linalg.norm(x, axis=1)[:, numpy.newaxis]

Кстати, хорошая ссылка для просмотра: http://www-alg.ist.hokudai.ac.jp/~jan/randsphere.pdf

Что касается равномерного распределения внутри сферы, вместо нормализации вектора, вы должны умножить vercor на некоторый f (r): f (r) * r распределяется с плотностью, пропорциональной r ^ n на [0,1], что было сделано в опубликованном вами коде

Ответ 4

Будет ли это достаточно однородным для ваших целей?

In []: p= 2* rand(3, 1e4)- 1
In []: p= p[:, sum(p* p, 0)** .5<= 1]
In []: p.shape
Out[]: (3, 5216)

Его срез

In []: plot(p[0], p[2], '.')

выглядит так: enter image description here

Ответ 5

Нормированный гауссовский вектор 3d равномерно распределен по сфере, см. http://mathworld.wolfram.com/SpherePointPicking.html

Например:

N = 1000
v = numpy.random.uniform(size=(3,N)) 
vn = v / numpy.sqrt(numpy.sum(v**2, 0))

Ответ 6

Я согласен с Аллео. Я перевел ваш код Matlab на Python, и он может генерировать тысячи точек очень быстро (доля секунды на моем компьютере для 2D и 3D). Я даже запустил его до 5D гиперсфер. Я нашел ваш код настолько полезным, что я применяю его в исследовании. Тим Макджилтон, , кого я должен добавить в качестве ссылки?

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

единообразный образец

Ответ 7

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

Ответ 8

Мне нужно было пробовать точки в сфере в вычислительном шейдере.

Придумав мое собственное редуктивное решение, которое должно оказаться полезным для любого прототипирования, обратите внимание, что преобразующее решение даст лучший подход.

Этот подход прост, он использует любой шум или случайную функцию, и так же эффективен. Да, он отображает 3D-сферу.

float3 randomInsideUnitSphere()
{
    // Calculate 3 random components for the vector in the range [-1, 1]
    float3 v = float3(randomNeg1To1(), randomNeg1To1(), randomNeg1To1());

    if (dot(v, v) > 1.0)
    {
        v = float3(0, 0, 0);
    }

    return v;
}

Ответ 9

Вы можете просто генерировать случайные точки в сферических координатах (при условии, что вы работаете в 3D): S (r, θ, φ), где r ∈ [0, R), θ ∈ [0, π], φ ∈ [0, 2π), где R - радиус вашей сферы. Это также позволит вам напрямую контролировать, сколько точек создано (т.е. Вам не нужно отбрасывать какие-либо очки).

Чтобы компенсировать потерю плотности радиусом, вы должны генерировать радиальную координату после распределения степенного закона (см. dmckee answer для объяснения того, как это сделать).

Если ваш код нуждается в координатах (x, y, z) (т.е. декартовых), вы тогда просто преобразуете случайно сгенерированные точки в сферические в декартовы координаты, как объяснено .