js在球面上随机均匀分布点的算法

一个简单的DEMO展示了如何在球面上随机均匀分布点,2022年更新了新版本,点击这里查看更新的内容

基于变换抽样的Marsaglia方法

效果最好,最均匀的算法,但是它其实是一种伪随机数生成器

伪随机数生成器(pseudo random number generator,PRNG),又被称为确定性随机比特生成器(deterministic random bit generator,DRBG),是一个生成数字序列的算法,其特性近似于随机数序列的特性。PRNG生成的序列并不是真随机,因此它完全由一个初始值决定,这个初始值被称为PRNG的随机种子(seed,但这个种子可能包含真随机数)。尽管接近于真随机的序列可以通过硬件随机数生成器生成,但伪随机数生成器因为其生成速度和可再现的优势,在实践中也很重要。

大致的步骤如下

1
2
3
4
5
6
7
8
9
10
11
12
13
step1:
    随机抽样产生一对均匀分布的随机数 u ,v ;这里u,v 在[-1,1] 范围内
 step2 :
    计算 r^2 = u^2+v^2;
    如果 r^2 > 1 则重新抽样,直到满足 r^2 < 1 .
step3 :
    计算
  
    x=2*u*sqrt(1-r2);

    y=2*v*sqrt(1-r2);

    z=1-2*r2;  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

const getRandomFloat = (min, max) => Math.random() * (max - min) + min;

/**
* 基于变换抽样的Marsaglia方法
* @param maxRadius
* @param num
* @return {{x:number,y:number,z:number}[]}
* @constructor
*/
export function MarsagliaRandom(maxRadius,num) {
let res = []
num = num || 100
for (let i=0;i<num;){
let x1,x2
while (true){
x1 = getRandomFloat(0,maxRadius)
x2 = getRandomFloat(0,maxRadius)
x1 = 2*(x1-0.5)
x2 = 2*(x2-0.5)
if((x1*x1+x2*x2)<1) {
i++
break
}
}
let obj = {
x:(2*x1*Math.sqrt(1-x1*x1-x2*x2))*maxRadius,
y:(2*x2*Math.sqrt(1-x1*x1-x2*x2))*maxRadius,
z:(1-2*(x1*x1+x2*x2))*maxRadius,
}
res.push(obj)
}
return res
}

一次随机角度和方向的方法

效果不是很好,但效率稍高一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 随机角度和方向的方法
* @param maxRadius
* @param num
* @return {{x:number,y:number,z:number}[]}
* @constructor
*/
export function Simple2SphereRandom(maxRadius,num) {
let res = []
num = num || 100
let rotval,phi,theta
const {sin,cos} = Math
for (let i=0;i<num;i++){
let obj = {}
rotval = getRandomFloat(0,maxRadius)
theta = Math.PI * rotval
rotval = getRandomFloat(0,maxRadius)
phi = 2*Math.PI*rotval

obj.x = sin(theta)*cos(phi) * maxRadius
obj.y = sin(theta)*sin(phi) * maxRadius
obj.z = cos(theta) * maxRadius

res.push(obj)
}
return res
}