如何生成范围为(-1,1)以使总和为0的随机值?

时间:2019-10-16 07:16:31

标签: python random

如果总和为1,我可以将这些值除以总和。但是,当总和为0时,此方法不适用。

也许我可以计算每个采样值的相反值,所以我总是会有一对数字,使得它们的总和为0。但是,这种方法减少了我希望在随机数组中拥有的“随机性”。

有更好的方法吗?

编辑:数组的长度可以变化(从3到几百个),但是在采样之前必须固定。

5 个答案:

答案 0 :(得分:1)

有一个 Dirichlet-Rescale (DRS) algorithm 可以生成随机数总和为给定的数字。正如它所说,它具有

<块引用>

向量均匀分布在有效区域上 所有可能向量的域,受约束。

它还有一个 Python library

答案 1 :(得分:0)

我会尝试以下解决方案:

def draw_randoms_while_sum_not_zero(eps):
    r = random.uniform(-1, 1)
    sum = r
    yield r
    while abs(sum) > eps:
        if sum > 0:
            r = random.uniform(-1, 0)
        else:
            r = random.uniform(0,1)
        sum += r
        yield r

由于浮点数不是十分准确,所以您永远无法确定要绘制的数字总和为0。您需要确定可接受的边距,然后调用上述生成器。

只要不等于0 ± eps,它就会根据您的需要产生(延迟返回)随机数

epss = [0.1, 0.01, 0.001, 0.0001, 0.00001]

for eps in epss:
    lengths = []
    for _ in range(100):
        lengths.append(len(list(draw_randoms_while_sum_not_zero(eps))))
    print(f'{eps}: min={min(lengths)}, max={max(lengths)}, avg={sum(lengths)/len(lengths)}')

结果:

0.1: min=1, max=24, avg=6.1
0.01: min=1, max=174, avg=49.27
0.001: min=4, max=2837, avg=421.41
0.0001: min=5, max=21830, avg=4486.51
1e-05: min=183, max=226286, avg=48754.42

答案 2 :(得分:0)

生成此类列表的一种方法是使用相反的数字。 如果这不是理想的属性,则可以通过向不同的相反对添加/减去相同的随机值来引入一些额外的随机性,例如:

No module named 'lib'

编辑

如果上限和下限不是严格的,构造零和序列的一种简单方法是将两个单独的序列求和归一化为1和-1并将它们连接在一起:

def exact_sum_uniform_random(num, min_val=-1.0, max_val=1.0, epsilon=0.1):
    items = [random.uniform(min_val, max_val) for _ in range(num // 2)]
    opposites = [-x for x in items]
    if num % 2 != 0:
        items.append(0.0)
    for i in range(len(items)):
        diff = random.random() * epsilon
        if items[i] + diff <= max_val \
                and any(opposite - diff >= min_val for opposite in opposites):
            items[i] += diff
            modified = False
            while not modified:
                j = random.randint(0, num // 2 - 1)
                if opposites[j] - diff >= min_val:
                    opposites[j] -= diff
                    modified = True
    result = items + opposites
    random.shuffle(result)
    return result


random.seed(0)
x = exact_sum_uniform_random(3)
print(x, sum(x))
# [0.7646391433441265, -0.7686875811622043, 0.004048437818077755] 2.2551405187698492e-17

请注意,两种方法通常都不会具有统一的分布。

答案 3 :(得分:0)

您可以使用sklearns Standardscaler。它会将您的数据缩放为方差为1,平均值为0。平均值为0等于总和为0。

from sklearn.preprocessing import StandardScaler, MinMaxScaler
import numpy as np
rand_numbers = StandardScaler().fit_transform(np.random.rand(100,1, ))

如果您不想使用sklearn,则可以手动进行标准化,该公式非常简单:

rand_numbers = np.random.rand(1000,1, )
rand_numbers = (rand_numbers - np.mean(rand_numbers)) / np.std(rand_numbers)  

这里的问题是1的方差,它导致数字大于1或小于-1。因此,您可以通过其最大abs值来定义数组。

rand_numbers = rand_numbers*(1/max(abs(rand_numbers)))

现在您有了一个数组,其值介于-1和1之间,总和实际上接近于零。

print(sum(rand_numbers))
print(min(rand_numbers))
print(max(rand_numbers))

输出:

[-1.51822999e-14]
[-0.99356294]
[1.]

此解决方案将使您的数据始终保持1或1 -1的状态。如果您想避免这种情况,可以通过最大吸收将正随机因子添加到除法中。 rand_numbers*(1/(max(abs(rand_numbers))+randomfactor))

修改

正如@KarlKnechtel所提到的,用标准偏差进行除法与用最大绝对值进行除法是多余的。

以上操作可以通过以下方式简单完成:

rand_numbers = np.random.rand(100000,1, )
rand_numbers = rand_numbers - np.mean(rand_numbers)
rand_numbers = rand_numbers / max(abs(rand_numbers))

答案 4 :(得分:0)

由于您可以生成大量数字并除以和,所以为什么不生成n / 2个正数除以和。然后生成n / 2个负数并除以和?

是否需要从正到负的随机组合?首先随机生成该混合,然后继续。