生成随机数a,b,c,使得a ^ 2 + b ^ 2 + c ^ 2 = 1

时间:2017-08-16 11:11:59

标签: python random

要在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不熟悉,我不确定如何做到这一点。允许否定。

编辑:非常感谢您的答案!

6 个答案:

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

结果:

enter image description here

说明:行分别显示abc的分布,而列显示不同方法的直方图(分布)。

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