我有一个组合问题,我希望能够随机选择一个介于0和一个大整数之间的整数。
我当前方法的不足之处
现在,对于常规整数,我通常会编写类似int rand 500;
的东西并完成它。
但是对于大整数,看起来rand
并非为此目的。
使用以下代码,我对rand $bigint
进行了200万次调用的模拟:
$ perl -Mbigint -E 'say int rand 1230138339199329632554990773929330319360000000 for 1 .. 2e6' > rand.txt
结果集的分布远非理想的:
0(56个计数)
震级1e + 040(112个计数)
震级1e + 041(1411个计数)
1e + 042级(14496个计数)
震级1e + 043(146324计数)
震级1e + 044(1463824个计数)
1e + 045级(373777计数)
因此,该过程永远无法选择
999
或5e+020
之类的数字,这使此方法不适合我要执行的操作。看起来这与
rand
的任意精度有关,在我的测试过程中,精度从未超过15位:$ perl -E 'printf "%.66g", rand'
0.307037353515625
如何克服此限制?
我最初的想法是,也许有一种方法可以影响
rand
的精度,但是它感觉像是一个创可贴,可以解决更大的问题(即rand
无法处理大整数)。无论如何,我希望有人以前走过这条路,并且知道如何解决这种情况。
最佳答案
(从我的评论转换)
一种更受理论驱动的方法是使用对PRNG的多次调用来创建足够的随机位供您的数字进行采样。如果某些PRNG产生的位数不等于下面概述的所需位数,则必须小心!
伪码
计算代表您的数字所需的位:n_needed_bits
检查PRNG返回的位大小:n_bits_prng
计算所需的样本数:needed_prng_samples = ceil(n_needed_bits / n_bits_prng)
虽然为真:
采样needed_prng_samples
(调用PRNG)时间并连接所有获得的位
检查结果数字是否在您的范围内
是?:返回编号(已完成)
否?:什么也不做(循环继续;将再次对所有组件重新采样!)
备注
这是acceptance-sampling / rejection-sampling的一种形式
方法是Las-vegas type of algorithm:理论上不受运行时的限制
平均需要的循环数:n_possible-sample-numbers-of-full-concatenation / n_possible-sample-numbers-within-range
根据拒绝方法进行完整的重采样(如果结果不在范围内),可以进行更正式的非偏置/均匀性分析,这对于此方法而言非常重要
当然,要使这项工作需要有关PRNG输出的经典假设。
例如,如果PRNG在低位/高位方面(如常提到的)有一些不均匀性,则会对上面的输出产生影响
关于perl - 如何选择0到bigint之间的随机值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39958739/