为测试生成强偏差随机数

时间:2012-10-07 02:13:38

标签: random lua automated-tests distribution

我想用随机输入运行测试,需要生成'明智'随机 数字,即匹配足以传递测试函数的数字 先决条件,但希望在其代码中深入破坏。

math.random()(我正在使用Lua)产生均匀分布的随机数 数字。扩大这些数字将提供比小数字更大的数字, 并且整数很少。

我想扭曲随机数(或使用旧数生成新数据) 作为一个随机性来源,以一种强烈支持“简单”数字的方式, 但仍将覆盖整个范围,即延伸至正/负无穷大 (或±1e309代表double)。这意味着:

  • 数字最多,比方说,十分应该是最常见的,
  • 整数应该比分数更常见,
  • 以0.5结尾的数字应该是最常见的分数,
  • 接着是0.25和0.75;那么0.125,
  • 等等。

不同的描述:修复基本概率 x 以使概率 总和为1并将数字 n 的概率定义为 x k 其中 k 是将 n 构造为超现实主义的一代 数 1 。将 x 指定为0,将 x 2 指定为-1和+1, x 3 到-2,-1 / 2,+ 1/2和+2,依此类推。这个 给出了一个很接近我想要的东西的描述(它也有点偏斜 很多),但几乎无法用于计算随机数。所结果的 分配是无处不在的(它是分形的!),我不知道如何 确定基本概率x(我认为它的无限精度 零),并且通过迭代计算基于此的数字是非常的 慢(花费近乎无限的时间来构建大数字)。

有人知道一个简单的近似,给定均匀分布 随机源,产生非常粗略分布的随机数 如上所述?

我想进行数以千计的随机测试,数量/速度更快 比质量重要。不过,更好的数字意味着更少的投入被拒绝。

Lua有一个JIT,因此性能通常不是问题。但是,基于跳跃 关于随机性将打破每个预测,并且许多调用math.random() 也会很慢。这意味着封闭的公式将优于a 迭代或递归的。


1 维基百科有一个article on surreal numbers 一个nice picture。一个超现实的数字是一对两个超现实主义 数字,即x := {n|m},其值是中间的数字 对,即(对于有限数){n|m} = (n+m)/2(理性)。如果是一方 该对是空的,这被解释为增量(或减少,如果正确的话) 一个人是空的。如果双方都是空的,那就是零。最初,有 没有数字,所以唯一可以建立的是0 := { | }。在一代 两个人可以建立数字{0| } =: 1{ |0} =: -1,我们得到三个 {1| } =: 2{|1} =: -2{0|1} =: 1/2{-1|0} =: -1/2(加上一些 更复杂的已知数字表示,例如{-1|1} ? 0)。注意 例如1/3永远不会由有限数生成,因为它是无限的 分数 - 浮点数相同,1/3从未完全表示。

3 个答案:

答案 0 :(得分:1)

算法怎么样?

  1. 使用库函数
  2. 在(0,1)中生成随机浮点数
  3. 根据所需的概率密度函数生成随机积分舍入点(例如0为概率0.5,1概率为0.25,2概率为0.125,......)。
  4. 将该浮点数'舍入'(例如floor((float_val << roundoff)+0.5)
  5. 根据另一个PDF生成随机积分指数(例如0,1,2,3,概率为0.1,之后逐渐减小)
  6. 将圆形浮点乘以2 exponent

答案 1 :(得分:1)

对于类似超现实的十进制扩展,您需要一个随机二进制数。 偶数位告诉您是停止还是继续,奇数位告诉您是在树上向右还是向左:

> 0... => 0.0 [50%] Stop
> 100... => -0.5 [<12.5%] Go, Left, Stop
> 110... => 0.5 [<12.5%] Go, Right, Stop
> 11100... => 0.25 [<3.125%] Go, Right, Go, Left, Stop
> 11110... => 0.75 [<3.125%] Go, Right, Go, Right, Stop
> 1110100... => 0.125
> 1110110... => 0.375
> 1111100... => 0.625
> 1111110... => 0.875

快速生成随机二进制数的一种方法是查看math.random()中的十进制数字,并将0-4替换为'1',将5-9替换为'1':

  • 0.8430419054348022 变 1000001010001011 变为-0.5

  • 0.5513009827118367 变 1100001101001011 变为0.25

没有做过多的lua编程,但在Javascript中你可以这样做:

Math.random().toString().substring(2).split("").map(
    function(digit) { return digit >= "5" ? 1 : 0 }
);

或真正的二进制扩展:

Math.random().toString(2).substring(2)

不确定哪个更真实“随机” - 你需要测试它。

可能以这种方式生成超现实数字,但大多数结果将是a / 2 ^ b形式的小数,整数相对较少。在第3天,只产生2个整数(-3和3),相对于6个小数,在第4天,它是2对14,在第n天,它是2对(2 ^ n-2)。

如果你从math.random()添加两个统一的随机数,你会得到一个新的分布,它有一个像分布一样的“三角形”(从中心线性减少)。添加3个或更多将获得更多“钟形曲线”,如以0为中心的分布:

math.random() + math.random() + math.random()  - 1.5

除以随机数将得到一个真正的数字:

A/(math.random()+1e-300)

这将返回A和(理论上)A * 1e + 300之间的结果, 虽然我的测试显示50%的时间结果在A和2 * A之间 大约75%的时间介于A和4 * A之间。

把它们放在一起,我们得到:

round(6*(math.random()+math.random()+math.random() - 1.5)/(math.random()+1e-300))

这超过70%的数字在-9和9之间返回,很少有大数字出现。

请注意,此分布的平均值和总和趋向于偏向大的负数或正数,因为运行它的次数越多,分母中的小数字就越有可能导致数字“爆炸“大量涌现,例如147,967或-194,137。

请参阅gist了解示例代码。

Josh

答案 2 :(得分:0)

您可以立即计算第n个出生的超现实数字。

例如,第1000个超现实数字是:

  1. 转换为二进制:

    1000年12月= 1111101000 bin

  2. 1变成加号,0变成负号

    1111101000

    +++++-+ ---

  3. 第一个'1'位是0值,下一组相似的数字是+1(代表1)或-1(代表0),然后值是1 / 2、1 / 4、1 / 8,以此类推。

    1 1 1 1 1 0 0 0 0 0

    ++ + + +-+---

    0 1 1 1 1 h h h h h h

    + 0 + 1 + 1 + 1 + 1-1 / 2 + 1 / 4-1 / 8-1 / 16-1 / 32

    = 3 + 17/32

    = 113/32

    = 3.53125

此表示形式的二进制长度(以位为单位)等于该数字的出生日期。

一个超现实数字的左,右数字是二进制表示形式,其尾部分别剥去了最后的0或1。

超现实数字在-1和1之间均匀分布,其中存在到特定日期创建的数字的一半将存在。 1/4的数字均匀分布在-2至-1和1至2之间,依此类推。最大范围是从负数到正整数,与您提供的天数相匹配。这些数字缓慢地变为无穷大,因为每天只向负数和正数范围加一个,并且天数是最后一个数的两倍。

编辑:

此位表示形式的好称呼是“ sinary”

负数是换位。例如:

100010101001101s -> negative number (always start 10...)

111101010110010s -> positive number (always start 01...)

我们注意到所有位翻转都接受第一个换位。

Nan => 0s(因为所有其他数字均以1开头),这非常适合在计算机的位寄存器中表示,因为需要前导零(我们不再将三进制计算机变成……太糟糕了)

所有Conway超现实代数都可以在这些数字上完成,而无需转换为二进制或十进制。

正弦格式可以看做是一个加一个简单的计数器并附加一个2的补码十进制表示形式。

以下是关于结业的不完整报告(类似于罪恶):https://github.com/peawormsworth/tools/blob/master/finary/Fine%20binary.ipynb