为测试生成强烈偏向的随机数

Posted

技术标签:

【中文标题】为测试生成强烈偏向的随机数【英文标题】:Generating strongly biased random numbers for tests 【发布时间】:2012-09-27 17:59:44 【问题描述】:

我想使用随机输入运行测试,并且需要生成“合理”随机 数字,即匹配得足够好以通过测试函数的数字 先决条件,但希望在其代码内部造成更严重的破坏。

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

我想歪曲随机数(或使用旧的生成新的 作为随机源)以强烈支持“简单”数字的方式, 但仍将覆盖整个范围,即扩展到正/负无穷大 (或±1e309double)。这意味着:

最多十个数字应该是最常见的, 整数应该比分数更常见, 以 0.5 结尾的数字应该是最常见的分数, 后跟 0.25 和 0.75;然后是 0.125, 等等。

不同的描述:固定一个基本概率 x 使得概率 将总和为 1 并将数字 n 的概率定义为 xk 其中 kn 被构造为超现实的一代 号码1。将 x 分配给 0,将 x2 分配给 -1 和 +1, x3 到 -2、-1/2、+1/2 和 +2,依此类推。这 很好地描述了接近我想要的东西(它也有点歪斜 很多),但几乎无法用于计算随机数。所结果的 分布不是连续的(它是分形的!),我不知道如何 确定基本概率x(我认为对于无限精度,它会是 零),并且基于此通过迭代计算数字非常糟糕 慢(花费近乎无限的时间来构造大量数字)。

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

我想运行数千个随机测试,数量/速度更多 比质量重要。尽管如此,更好的数字意味着更少的输入被拒绝。

Lua 有 JIT,所以性能通常不是什么大问题。然而,基于跳跃 随机性会破坏每一个预测,以及对math.random()的许多调用 也会很慢。这意味着封闭式公式将优于 迭代或递归。


1 ***有一个article on surreal numbers,其中 nice picture。一个超现实的数字是一对两个超现实的 数字,即x := n|m,其值为中间的数字 对,即(对于有限数)n|m = (n+m)/2(作为有理数)。如果一侧 对是空的,这被解释为增量(或减量,如果正确 是空的)减一。如果两边都是空的,那就是零。最初,有 没有数字,所以唯一可以建立的数字是0 := | 。在一代 二一可以建立数字0| =: 1 |0 =: -1,三我们得到 1| =: 2|1 =: -20|1 =: 1/2-1|0 =: -1/2(加上一些 已知数字的更复杂的表示,例如-1|1 ? 0)。注意 例如1/3 永远不会由有限数生成,因为它是无限的 分数 - 浮点数也是如此,1/3 永远不会精确表示。

【问题讨论】:

需要注意的一点是,浮点数在其范围内不是均匀分布的。 【参考方案1】:

算法怎么样?

    使用库函数在 (0, 1) 中生成随机浮点数 根据所需的概率密度函数生成一个随机整数舍入点(例如,0 代表概率 0.5,1 代表概率 0.25,2 代表概率 0.125,...)。 按该舍入点“舍入”浮点数(例如floor((float_val << roundoff)+0.5)) 根据另一个 PDF 生成一个随机积分指数(例如 0、1、2、3,每个概率为 0.1,然后递减) 将四舍五入的浮点数乘以 2exponent

【讨论】:

【参考方案2】:

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

> 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

【讨论】:

【参考方案3】:

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

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

    转换为二进制:

    1000 dec = 1111101000 箱

    1 成为加号,0 成为减号:

    1111101000

    +++++-+---

    第一个'1'位是0值,下一组相似的数字是+1(代表1)或-1(代表0),那么值是1/2、1/4、1/8,等每个后续位。

    1 1 1 1 1 0 1 0 0 0

    + + + + + - + - - -

    0 1 1 1 1 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 是 => 0(因为所有其他数字都以 1 开头),这使得它非常适合在计算机中的位寄存器中表示,因为需要前导零(我们不再制造三进制计算机......太糟糕了)

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

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

这里是关于finary的不完整报告(类似于sinary):https://github.com/peawormsworth/tools/blob/master/finary/Fine%20binary.ipynb

【讨论】:

以上是关于为测试生成强烈偏向的随机数的主要内容,如果未能解决你的问题,请参考以下文章

生成2个随机数

实用的随机数生成类Random:测试(随机产生100个不重复的正整数)

每个父行的随机基数

在 Javascript 中播种随机数生成器

如何为 scikit-learn 播种随机数生成器?

如何使用 FsCheck 生成随机数作为基于属性的测试的输入