Perl 如何在0和bigint之间选择一个随机值?

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 结果集的分布远远不

我有一个组合数学问题,我想能够在0和一个大整数之间随机选取一个整数


我目前方法的不足之处 现在对于正则整数,我通常会写一些像
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