web-dev-qa-db-ja.com

単位球の表面に分布する点のランダムサンプルを生成する

私はnumpyを使用して球の表面にランダムな点を生成しようとしています。均一分布について説明している投稿を確認しました here 。ただし、球の表面にのみポイントを生成する方法についてのアイデアが必要です。私は座標(x、y、z)とこれらの各球の半径を持っています。

私はこのレベルの数学に精通しておらず、モンテカルロシミュレーションを理解しようとしています。

どんな助けでも大歓迎です。

ありがとう、Parin

18
Parin

このページの最後のアプローチ に基づいて、3つの標準正規分布から独立したサンプルで構成されるベクトルを生成し、その大きさが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

例えば:

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

同じ方法は、単位円(ndim=2)または高次元の単位超球の表面。

30
ali_m

球の表面上の点は、2つの球面座標thetaおよびphiを使用して、_0 < theta < 2pi_および_0 < phi < pi_で表すことができます。

デカルト_x, y, z_座標への変換式:

_x = r * cos(theta) * sin(phi)
y = r * sin(theta) * sin(phi)
z = r * cos(phi)
_

ここで、rは球の半径です。

そのため、プログラムは、thetaphiを一定の範囲でランダムにサンプリングし、そこからデカルト座標を生成できます。

しかし、その後、点は球の極上でより多くのデンスリーに分散されます。ポイントが球の表面で均一に分布するためには、phiphi = acos(a)として選択する必要があります。ここで、_-1 < a < 1_は均一な分布で選択されます。

Numpyコードの場合、変数radiusが固定値であることを除いて、 球形ボリューム内の一様分布ランダムポイントのサンプリング と同じです。

8
tmlen

@Soontsとの議論に続いて、回答で使用されている3つのアプローチのパフォーマンスに興味を持ちました。

これが私の試みた比較です:

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

次に1000ポイント

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

拒否ベースの実装では、最初にnpointsサンプルを生成し、不良なサンプルを破棄しました。ループを使用して残りのポイントを生成しただけであることに注意してください。直接の段階的な拒否には長い時間がかかるように思われました。また、ゼロによる除算のチェックを削除して、sample_normalsケースとの比較を明確にしました。


2つの直接的な方法からベクトル化を削除すると、それらは同じ球場に入ります。

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

ハードウェアに応じてはるかに高速になる可能性がある別の方法。

a, b, cを選択して、それぞれ-1から1の間の3つの乱数にします

計算r2 = a^2 + b^2 + c^2

R2> 1.0(=点が球内にない)またはr2 <0.00001(=点が中心に近すぎる場合、球の表面に投影するときにゼロで除算します)の場合、値を破棄します、別のランダムセットを選択しますa, b, c

それ以外の場合は、(球の中心を基準にして)ランダムな点が得られます。

ir = R / sqrt(r2)
x = a * ir
y = b * ir
z = c * ir
2
Soonts

(コメントからの修正を反映するように編集)

2004年に、この問題に対するいくつかの一定時間アプローチを調査しました。

球座標で作業していると仮定します。ここで、thetaは垂直軸の周りの角度(経度など)であり、phiは赤道から上った角度(緯度など)であり、赤道の北側にある半球上のランダムな点の均一な分布:

  1. theta = Rand(0、360)を選択します。
  2. phi = 90 *(1-sqrt(Rand(0、1)))を選択します。

半球ではなく球上のポイントを取得するには、50%の時間だけphiを無効にします。

好奇心旺盛な方のために、ユニットディスク上に均一に分散されたポイントを生成するための同様のアプローチがあります。

  1. theta = Rand(0、360)を選択します。
  2. radius = sqrt(Rand(0、1))を選択します。

私はこれらのアプローチの正しさを証明するものはありませんが、過去10年ほどの間に多くの成功を収めて使用してきましたが、それらの正しさは確信しています。

さまざまなアプローチのいくつかの図(2004年から)は here です。これには、立方体の表面上の点を選択し、それらを球に正規化するアプローチの視覚化が含まれます。

0
orion elenzil