【问题标题】:transforming random.random uniform to exponential distribution doesn't produce correct result将 random.random 均匀转换为指数分布不会产生正确的结果
【发布时间】:2020-11-04 07:47:30
【问题描述】:

我正在尝试生成一个合成地震数据库,其中震级 ($M$) 在 $[M, M+\delta_M]$ 范围内的事件数 ($N$) 如下:
$\log_{10}(N) = a - bM$
其中 $a$ 和 $b$ 是常量。

我正在尝试使用 random 模块在 Python 中执行此操作。我知道我可以(或者至少我认为我可以 - 因为我还没有尝试过)使用 random.expovariate 但我认为我可以使用 random.random 进行如下转换:

-math.log10(random.random()))

我对 2,000,000 个样本运行此程序,然后将这些样本分箱到 0.1 个箱中并以对数刻度绘制。

红线表示用于生成合成样本的理论分布。

我不担心 x=4.5 以上的变化。这是由于点数少和自然随机性所致。我要问的是 x=0 附近点的非常小的(在这个比例下)变化。我从理论(蓝点)绘制了合成点的变化:

随着 x 的减少,事件的数量呈指数增长,因此与理论的变化应该减少 - 而不是增加。而 x=0 处的点则相反。

为了尝试找出我的问题所在,我编写了生成从 0 到 1 的数字的代码,步骤非常精细。然后每个数字都经过上述功能。结果(上图中的蓝点)是纯线性的,与理论值完全匹配。这说明我的转换函数和代码都没有问题。

所以上图中的 twp 点集之间的唯一区别是蓝色点是由对随机函数的 2,000,000 次调用生成的(然后将结果转换为幅度并分箱),而对于红色点我已经在 0 和 1 之间采取 2,000,000 步均匀(然后将结果转换为幅度并使用相同的代码进行分箱)。

所以我认为这与随机数生成器有关?

如果有任何指点,将不胜感激。谢谢。

[添加] 按照@Arty 的建议,将调用从random.random 更改为random.uniform(0,1),错误现在是对称分布的并且具有预期的大小。已在图中添加了 +- 1 个标准差。

显然random()uniform(0,1) 的做法略有不同。


我使用random.randomrandom.uniform(0,1)np.random.randomnp.random.uniform(0, 1) 减少了我的代码并计算了合成数据,获得了 2,000,000 分。

对结果进行分箱并绘制观察到的数字与预期数字之间的差异(如下)。

还添加了 +-1 标准偏差限制。这些数字都是对称分布的,并且大小正确,表明所有随机生成器都工作正常。

我的结论是,在更改/优化代码的某个地方,我引入了一个现在已经丢失的问题。我非常想找到那个错误,所以我不会再犯了!

令我惊讶的是,我原来的错误代码能够正确执行,以至于它生成了一个看起来真实的合成,只有难以检测的轻微异常。

感谢大家的帮助,并向那些我不同意我说问题不在于随机数生成器的人道歉!

【问题讨论】:

  • 您能否提供一个minimal reproducible example,说明您是如何执行生成、累积和分箱的?我认为两个最可能的原因是非正规数(在 0 附近提供更大范围的可能值;我还没有考虑到足以确定这是一个真正的可能性)或分箱中的错误,但这将有助于查看代码。
  • 你能用你的代码测试np.random.uniform(0, 1)的随机性吗?它使用不同的算法和代码进行生成。有趣的是它是否会给出与random.random() 相同的结果。如果您提供执行此图表和测量的完整代码,也很好。
  • @RustyC 好的。但请注意,这很可能是一个统计问题——包括各种随机分布的特征。即使将您的比较作为 uniform 样本,您也已经引入了偏差,这可能不是您真正想要的。您的分箱偏向统一,但数据偏向小值(即,分箱中的数据不居中且不统一)。 Stack Overflow 适合找出为什么 random.random 会偏离均匀分布,但不会在均匀分布不适合问题时警告您。
  • 我看不出random.uniform(0,1) 会如何影响事情,它只会评估0 + 1 * random.random() 所以应该会慢一点
  • @RustyC 你说“显然 random() 和 uniform(0,1) 做的事情略有不同。”但我已经链接到 CPython 代码,我们可以看到它没有做任何不同的事情

标签: python random


【解决方案1】:

最初我以为您可能遇到了一些数值分析问题。然而,在 python 中尝试一百万个样本,我得到以下观察结果:

>>> T = int(1e6)
>>> xs = [ -math.log10(random.random()) for i in range(T)]
>>> len([x for x in xs if 0 <= x < 0.1])
205614
>>> len([x for x in xs if 0.1 <= x < 0.2])
163736
>>> len([x for x in xs if 0.2 <= x < 0.3])
129627
>>> len([x for x in xs if 0.3 <= x < 0.4])
103413
>>> len([x for x in xs if 0.4 <= x < 0.5])
81734

如果 X = -log_10(x) 且 x 均匀分布在 [0, 1) 上,那么我们应该有

P(M <= X < M + d) = P(-M-d < log_10(x) <= -M) = 10^(-M) - 10^(-M-d)

而且上面的数字基本上完全符合这些概率,例如

1 - 10^(-0.1) = 0.205672

这与我们在上述 100 万次试验中观察到的 205614 次非常吻合。

你得到的结果与我上面的 python 代码不同吗?

【讨论】:

  • 那么你的解决办法是什么? random.random() 可以吗?如果不难的话,你也可以为np.random.uniform(0., 1.) 做同样的测量吗?这两个函数应该有不同的随机生成器实现。
  • @Arty:我相信random.random 正在按照我的机器上记录的方式工作,而且记录在案的行为使其成为 OP 目的的真正随机性的完美近似。由于 OP 没有详细说明问题中的图表所描绘的内容,因此我不能再多说了。至于numpy 随机生成器,我想它们并不比内置的python 差。
  • 顺便说一句,如果一个“将调用从 random.random 更改为 random.uniform(0,1)”,结果基本上不会改变 - 看到 random.uniform 如何缩放结果也就不足为奇了random.random.
  • @MisterMiyagi - 但它改变了结果!
  • @Daniel McLaury - 运行你的代码,我得到:205777 163508 129735 103058 82295 与你基本相同 - 在预期的随机变化范围内。
猜你喜欢
  • 2011-04-04
  • 2010-09-09
  • 2013-09-17
  • 2019-05-27
  • 1970-01-01
  • 2017-04-04
  • 2012-10-24
  • 2018-02-04
  • 2013-03-25
相关资源
最近更新 更多