要在Python中进行一些模拟,我尝试生成数字a,b,c,使a^2 + b^2 + c^2 = 1
。我认为在0到1之间生成一些a
,然后在0到b
之间生成一些sqrt(1 - a^2)
,然后c
= sqrt(1 - a^2 - b^2)
就可以了。
浮点值很好,平方和应该 close 为1.我想在一些迭代中继续生成它们。
对Python不熟悉,我不确定如何做到这一点。允许否定。
编辑:非常感谢您的答案!
答案 0 :(得分:8)
根据stats.stackexchange.com上的this回答,您应该使用正态分布值来在球体上获得均匀分布的值。这意味着,你可以这样做:
import numpy as np
abc = np.random.normal(size=3)
a,b,c = abc/np.sqrt(sum(abc**2))
答案 1 :(得分:8)
如果您对概率密度感兴趣,我决定对不同的方法进行比较:
import numpy as np
import random
import math
def MSeifert():
a = 1
b = 1
while a**2 + b**2 > 1: # discard any a and b whose sum of squares already exceeds 1
a = random.random()
b = random.random()
c = math.sqrt(1 - a**2 - b**2) # fixed c
return a, b, c
def VBB():
x = np.random.uniform(0,1,3) # random numbers in [0, 1)
x /= np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)
return x[0], x[1], x[2]
def user3684792():
theta = random.uniform(0, 0.5*np.pi)
phi = random.uniform(0, 0.5*np.pi)
return np.sin(theta)* np.cos(phi), np.sin(theta)*np.sin(phi), np.cos(theta)
def JohanL():
abc = np.random.normal(size=3)
a,b,c = abc/np.sqrt(sum(abc**2))
return a, b, c
def SeverinPappadeux():
cos_th = 2.0*random.uniform(0, 1.0) - 1.0
sin_th = math.sqrt(1.0 - cos_th*cos_th)
phi = random.uniform(0, 2.0*math.pi)
return sin_th * math.cos(phi), sin_th * math.sin(phi), cos_th
绘制分布图:
%matplotlib notebook
import matplotlib.pyplot as plt
f, axes = plt.subplots(3, 4)
for func_idx, func in enumerate([MSeifert, JohanL, user3684792, VBB]):
axes[0, func_idx].set_title(str(func.__name__))
res = [func() for _ in range(50000)]
for idx in range(3):
axes[idx, func_idx].hist([i[idx] for i in res], bins='auto')
axes[0, 0].set_ylabel('a')
axes[1, 0].set_ylabel('b')
axes[2, 0].set_ylabel('c')
plt.tight_layout()
结果:
说明:行分别显示a
,b
和c
的分布,而列显示不同方法的直方图(分布)。
在(-1, 1)
范围内提供均匀随机分布的唯一方法是JohanLs和Severin Pappadeux的方法。所有其他方法都具有一些功能,如尖峰或[0, 1)
范围内的功能行为。请注意,这两个解决方案目前的值介于-1和1之间,而所有其他方法的值介于0和1之间。
答案 2 :(得分:6)
我认为这实际上是一个很酷的问题,一个很好的方法就是使用球形极坐标并随机生成角度。
import random
import numpy as np
def random_pt():
theta = random.uniform(0, 0.5*np.pi)
phi = random.uniform(0, 0.5*np.pi)
return np.sin(theta)* np.cos(phi), np.sin(theta)*np.sin(phi), np.cos(theta)
答案 3 :(得分:4)
你可以这样做:
import random
import math
def three_random_numbers_adding_to_one():
a = 1
b = 1
while a**2 + b**2 > 1: # discard any a and b whose sum of squares already exceeds 1
a = random.random()
b = random.random()
c = math.sqrt(1 - a**2 - b**2) # fixed c
return a, b, c
a, b, c = three_random_numbers_adding_to_one()
print(a**2 + b**2 + c**2)
但float
只有有限的精度,所以这些不会恰好相加1
,只是差不多。
您可能需要检查使用此功能生成的数字是否足够随机"。可能是这种设置偏向了"随机性"。
答案 4 :(得分:3)
"对"答案取决于你是在空间,球体表面还是其他东西上寻找均匀的随机分布。如果您正在寻找球体表面上的点,您仍然需要担心cos(theta)
因素会导致点出现"聚集在一起"靠近球体的两极。由于你的问题不清楚确切的性质,这里是一个完全随机的"分配应该有效:
x = np.random.uniform(0,1,3) # random numbers in [0, 1)
x /= np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)
此处的另一个优点是,由于我们使用的是numpy数组,因此您可以使用x = np.random.uniform(0, 1, (3, n))
对任何n
快速扩展到大的点集。
答案 5 :(得分:3)
是时候添加另一种解决方案了,嘿......
这次在单位球体点选择上真正统一 - 检查http://mathworld.wolfram.com/SpherePointPicking.html了解详情
import math
import random
def random_pt():
cos_th = 2.0*random.uniform(0, 1.0) - 1.0
sin_th = math.sqrt(1.0 - cos_th*cos_th)
phi = random.uniform(0, 2.0*math.pi)
return sin_th * math.cos(phi), sin_th * math.sin(phi), cos_th
for k in range(0, 100):
a, b, c = random_pt()
print("{0} {1} {2} {3}".format(a, b, c, a*a + b*b + c*c))