Perl 如何在0和bigint之间选择一个随机值?
我有一个组合数学问题,我想能够在0和一个大整数之间随机选取一个整数Perl 如何在0和bigint之间选择一个随机值?,perl,random,bigint,Perl,Random,Bigint,我有一个组合数学问题,我想能够在0和一个大整数之间随机选取一个整数 我目前方法的不足之处 现在对于正则整数,我通常会写一些像intrand500并完成它 但是对于大整数来说,rand似乎不适用于此 使用下面的代码,我模拟了200万次调用rand$bigint: $ perl -Mbigint -E 'say int rand 1230138339199329632554990773929330319360000000 for 1 .. 2e6' > rand.txt 结果集的分布远远不
我目前方法的不足之处 现在对于正则整数,我通常会写一些像
intrand500代码>并完成它
但是对于大整数来说,rand
似乎不适用于此
使用下面的代码,我模拟了200万次调用rand$bigint
:
$ 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级(3777次计数)
因此,该过程无法选择像999
或5e+020
这样的数字,这使得这种方法不适合我想要做的事情
这似乎与rand
的任意精度有关,在我的测试过程中,该精度从未超过15位:
$ perl -E 'printf "%.66g", rand'
0.307037353515625
我如何克服这个限制?
我最初的想法是,可能有一种方法来影响<代码> RAND 的精度,但是它感觉像是一个更大的问题的辅助工具(也就是说,<代码> RAND 不能处理大整数)。
无论如何,我希望以前有人走过这条路,知道如何补救这种情况。
(根据我的评论转换)
一种更为理论化的方法是使用对PRNG的多次调用来创建足够的随机位,以便您的号码进行采样。如果某些PRNG产生的位数不等于下文所述的所需位数,则必须小心
伪码
- 计算表示数字所需的位:
n\u所需的位
- 检查PRNG返回的位的大小:
n\u bits\u PRNG
- 计算所需样本数:
needed\u prng\u samples=ceil(n\u needed\u bits/n\u bits\u prng)
- 尽管如此:
- Sample
needed\u prng\u samples
(对prng的调用)次数&连接所有获得的位
- 检查结果数字是否在您的范围内
- 是?:返回编号(已完成)
- 否?:不执行任何操作(循环继续;将再次对所有组件重新采样!)
评论
- 这是一种
- 这种方法是一种:运行时在理论上没有边界
- 所需的循环数为平均值:
n_可能-sample-numbers-of-full-concatenation/n_可能-sample-numbers-in-range
- 根据拒绝方法进行的完全重采样(如果结果不在范围内)可以对非偏差/均匀性进行更正式的分析,这是该方法的一个非常重要的方面
- 当然,要实现这一目标,需要对PRNG输出进行经典假设。
- 例如,如果PRNG在低位/高位(如经常提到的)方面具有一些不均匀性,这将对上述输出产生影响
一种方法是将数字的字符串表示形式切成块,当第一次随机抽取等于上限时,初始化的布尔($low)为false
编辑:在评论之后添加了一些解释
# first argument (in) upper bound
# second argument (in/out) is lower (false while random returns upper bound, after it remains true)
sub randhlp {
my($upp)=@_;
my $l=length $upp;
# random number less than
# - upper bound if islower is false
# - 9..99 otherwise
my $x=int rand ($_[1] ? 10**$l : $upp+1);
if ($x<$upp) {
$_[1]=1;
}
# left padding with 0
return sprintf("%0*d",$l,$x);
}
# returns a random number less than argument (numeric string)
sub randistr {
my($n)=@_;
$n=~/^\d+$/ or die "invalid input not numeric";
$n ne "0" or die "invalid input 0";
my($low,$x);
do {
undef $x;
# split string by chunks of 6 characters
# except last chunk which has 1 to 6 characters
while ($n=~/.{1,6}/g) {
# concatenate random results
$x.=randhlp($&,$low)
}
} while ($x eq $n);
$x=~s/^0+//;
return $x;
}
我从错误的角度看待这个问题
这些箱子大小不一样。每个箱子的大小是前一个箱子的10倍。从这个角度来看,对于每个具有幅值1e+40
的整数,在幅值1e+44
处可能有10000个整数
对于1e+45
处的bigint,找到任何数量级1e+20
的概率小于0.00000001%
忘了干草堆里的针吧,这更像是在类星体中找到一根针 你的环境是unix吗?你有/dev/random
和/dev/uradom
作为资源吗?@中缀它是Windows,所以没有(我习惯在发布到so之前用-E'
替换-E'
)?还有我的@simbabque打字错误。。。应该是1411,而不是1141A,更符合理论,但可能太慢了。方法:计算数字所需的位数(简单)。然后检查PRNG返回多少位;可能是32/64。采样ceil(所需字节/rng字节)时间并连接字节。如果结果值在您的范围内:接受;否则:重新采样(所有组件!;验收采样)。算法是合理的,但是你能取消代码吗?并删除从不为真的$low
,关联逻辑?$low在@数组中通过引用传递时变为真;示例上的逻辑如果输入为“12301383”,则将其分为“123013”和“83”。第一个随机数必须包含在000000和123013之间,如果小于123013,则下一个数字可以是介于00和99之间的任何数字,否则如果等于123013,则下一个数字必须介于00和83之间(除了这是最后一个块,所以它不能等于83)也许你可以重新表述它…无论如何,只是提醒一下,我相信你走错了路。Math::BigInt
是否取代了rand
?还有,Windows上的perl
版本?
for ($i=0;$i<2e6;++$i) {
$H{length(randistr("1230138339199329632554990773929330319360000000"))}+=1;
}
print "$_ $H{$_}\n" for sort keys %H;
39 4
40 61
41 153
42 1376
43 14592
44 146109
45 1463301
46 374404