2

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

math.random()(我正在使用 Lua)产生均匀分布的随机数。将这些按比例放大将产生比小数字更多的大数字,并且整数将非常少。

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

  • 最多十个数字应该是最常见的,
  • 整数应该比分数更常见,
  • 以 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() 也会很慢。这意味着封闭公式将比迭代或递归公式更好。


1维基百科有一篇关于超现实数字的文章,图片很好。一个超现实数是一对两个超现实数,即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} ? 01/3永远不会由有限数生成,因为它是一个无限小数——浮点数1/3也是如此,永远不会精确表示。

4

3 回答 3

1

这个算法怎么样?

  1. 使用库函数在 (0, 1) 中生成随机浮点数
  2. 根据所需的概率密度函数生成一个随机整数舍入点(例如,0 概率为 0.5,1 概率为 0.25,2 概率为 0.125,...)。
  3. 通过该舍入点“舍入”浮点数(例如floor((float_val << roundoff)+0.5)
  4. 根据另一个 PDF 生成一个随机积分指数(例如 0、1、2、3,每个概率为 0.1,然后递减)
  5. 将舍入浮点数乘以 2 exponent
于 2012-10-07T04:03:30.600 回答
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。

有关示例代码,请参阅要点

乔什

于 2012-10-26T06:27:48.090 回答
0

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

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

  1. 转换为二进制:

    1000 dec = 1111101000 仓

  2. 1 成为加号和 0 的减号:

    1111101000

    +++++-+---

  3. 第一个“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 哈哈哈哈哈

    +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 开头),这使得它非常适合在计算机中的位寄存器中表示,因为需要前导零(我们不再制作三进制计算机......太糟糕了)

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

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

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

于 2020-06-08T07:42:10.177 回答