【问题标题】:Fastest way to generate a number with a geometric distribution生成具有几何分布的数字的最快方法
【发布时间】:2020-01-19 23:50:11
【问题描述】:

我想生成一个伪随机数,其分布与在第一个反面之前为正面的硬币翻转次数相同。

0: p = 0.5
1: p = 0.25
2: p = 0.125
...

我怎样才能有效地制作这样的发行版?我一直在想办法做到这一点,而不会为每次翻转产生随机位。我目前的理论是计算均匀分布的随机数中前导 0 位的数量,但我一直无法找到一个好的参考或证明这是正确的。

【问题讨论】:

  • 您是否考虑过特定​​的编程语言、硬件或操作系统?
  • LZCNT 会起作用,但它显然会在整数的宽度处达到顶峰,而“直到第一个尾巴的翻转次数”的实际分布并没有达到顶峰。对于 64 位,差异非常小,但这是一个差异。那么它必须有多近?
  • @PeterO。 C,并且永远不需要超过 32 的结果,这就是整数宽度不会成为问题的原因。
  • @harold O(log log n),所以对于所有实际目的,32 位就足够了。
  • 如果你愿意涉足 C++ 有 std::geometric_distribution 或者如果你需要实数 std::exponential_distribution

标签: c random binary bit-manipulation


【解决方案1】:

这是正常的分解衰减分布。

您面临的问题不是如何确定这是正确的分布,而是生成具有此属性的随机生成器。通常,您以另一个随机数生成器为基础,并尝试应用一个函数,从该已知属性生成器中获取以新方式分布的数字。

对于您的方法,如果我对其应用函数,我已经多次使用标准随机生成器([0..1 之间的平面数字生成器,作为源),假设(只是猜测)@987654322 @function 你会得到一个大概像这样的图表:

|->    x
|->      x
|->         x
|->              x
|->                         x
|->                                                            x
+====================================================================

它似乎与所需积分的累积大致匹配(确实,它正确的方法)

这个函数的积分(给你 X

|->                                                            X
|->                         X
|->              x
|->         X     
|->      X                   
|->    X                                                        
+====================================================================
       0

它是反的(下面给出了一个对数函数,镜像在 XY 平分线上):

|          |
|    X     |
|          |
|  X       |
|X         |
+=============
 0         1

这是-log(1-x)(但-log(x) 就足够了,因为 x 在 0 和 1 之间是随机的)。

double y = -A * log(unit_random());

其中 A 是一些特定于产生更大或更小平均值的常数。 y 会得到预期的响应,unit_random() 始终是一个平坦分布,在单位区间内概率分布均等。

下面是一个完整的小程序示例,用于生成具有请求分布的点。功能

#include <math.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>

#include "gr.h"

double
geometric_random(void)
{
        double n = random();

        return -log(fabs(n) / INT_MAX);
} /* geometric_random */

产生所需的随机值,平均值为1.0。要获得M 的平均值,只需将其返回的值乘以M

complete example 发布在 github 上,带有一个 montecarlo 测试程序来测试分布属性。要执行它,请运行:

$ make
$ gr -n 10000000 | mc -n 100 -b 20 >mc.out

您将看到不同子区间的计数器比率在整个值范围内是如何保持不变的(嗯,不像预期的那样在低频下)

输出是:

                   n: 10000000
               sum_x: 10007209.0488715283572674
              sum_x2: 20023758.8835431151092052
               avg_x: 1.0007209048871528
              sdev_x: 1.0004667205707121
               min_x: 0.0000002696179218
               max_x: 17.2248827198513297
             below_A:      0
[0.0000000000000000, 0.2000000000000000]1811635
[0.2000000000000000, 0.4000000000000000]1484598: 0.8194796413184775
[0.4000000000000000, 0.6000000000000001]1213219: 0.8172037144061894
[0.6000000000000001, 0.8000000000000000]994937: 0.8200802987754066
[0.8000000000000000, 1.0000000000000000]813669: 0.8178095698521615
[1.0000000000000000, 1.2000000000000000]666035: 0.8185576690275775
[1.2000000000000000, 1.3999999999999999]545997: 0.8197722341918968
[1.3999999999999999, 1.5999999999999999]447841: 0.8202261184585263
[1.5999999999999999, 1.7999999999999998]365854: 0.8169283294740768
[1.7999999999999998, 1.9999999999999998]300525: 0.8214342333280489
[1.9999999999999998, 2.1999999999999997]246141: 0.8190366857998502
[2.1999999999999997, 2.3999999999999999]201303: 0.8178361183224250
[2.3999999999999999, 2.6000000000000001]164134: 0.8153579430013462
[2.6000000000000001, 2.8000000000000003]134768: 0.8210852108642938
[2.8000000000000003, 3.0000000000000004]110564: 0.8204024694289446
[3.0000000000000004, 3.2000000000000006] 90139: 0.8152653666654607
[3.2000000000000006, 3.4000000000000008] 74252: 0.8237499861325287
[3.4000000000000008, 3.6000000000000010] 60746: 0.8181059096051285
[3.6000000000000010, 3.8000000000000012] 49701: 0.8181773285483818
[3.8000000000000012, 4.0000000000000009] 40559: 0.8160600390334198
[4.0000000000000009, 4.2000000000000011] 33344: 0.8221109987918834
[4.2000000000000011, 4.4000000000000012] 27077: 0.8120501439539347
[4.4000000000000012, 4.6000000000000014] 22338: 0.8249806108505373
[4.6000000000000014, 4.8000000000000016] 18370: 0.8223654758707136
[4.8000000000000016, 5.0000000000000018] 15016: 0.8174197060424605
...

【讨论】:

  • 很好的答案,但是(我很抱歉没有让这个更清楚),我只对整数感兴趣,即。掷硬币的次数不能是分数。我可以将输出四舍五入吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-12
  • 1970-01-01
  • 2015-12-29
  • 1970-01-01
  • 2013-04-24
  • 2017-03-08
  • 2016-02-18
相关资源
最近更新 更多