【问题标题】:Why am I getting a bad random distribution with Perl's List::Util::shuffle?为什么 Perl 的 List::Util::shuffle 随机分布不好?
【发布时间】:2019-03-07 01:37:02
【问题描述】:

我收藏了数百张黑胶唱片,按目录 ID 字符串按字母数字排列。我编写了一个脚本,通过对目录 ID 的打乱数组进行采样,从我的收藏中随机选择 20 条记录。但是,我发现它为我选择的记录的分布通常并不好。很多时候,它会选择 2 条具有顺序目录 ID 的记录,和/或多个彼此靠近的记录。从 800 条记录中选择 20 条记录时,这种情况应该很少发生。

我将目录 ID 列表存储在 @selection 数组中,并从该数组中随机抽取 20 个项目,我从随机排列的数组中分配前 20 个项目:

@selection = (shuffle @selection)[0 .. 19];

无奈之下,我尝试了这种丑陋的技术来试图强制更好的随机性,但它似乎没有任何区别:

@selection = shuffle @selection; sleep 1;
@selection = reverse @selection; sleep 1;
@selection = (shuffle @selection)[0 .. 19];

【问题讨论】:

  • 我认为您看到了生日悖论en.m.wikipedia.org/wiki/Birthday_problem 的一种形式,确实,从 800 个中选择 20 个会导致两个相邻选择的频率比您预期的要高得多
  • 虽然在这种情况下这实际上不是您的问题,但如果您想要比 Perl 的默认 PRNG 更好的随机性来源,Math::Random::Secure 可能会很有用。获取 List::Util::shuffle 来使用它可能很棘手。

标签: arrays perl shuffle


【解决方案1】:

有 C(800, 20) = 3.73 × 1039 种方式从 800 中选择 20 个标题。

有 C(781, 20) = 2.29 × 1039 种方式从 800 个标题中选择 20 个标题,其中没有两个相邻。[1]

因此有 (2.29 × 1039) / (3.73 × 1039) = 61.4% 的机会选择不包含相邻标题的集合。

因此,有 1 - 61.4% = 38.6% 的机会选择包含相邻标题的集合。

现在我们知道会发生什么,让我们测试一下shuffle

测试:

#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw( shuffle );

my $num_tests = 100_000;
my $N = 800;
my @titles = 0..($N-1);
my $has_adjacent_titles = 0;
for (1..$num_tests) {
   my @shuffled_selection = ( shuffle(@titles) )[0..19];
   my @ordered = sort { $a <=> $b } @shuffled_selection;
   ++$has_adjacent_titles if grep { $ordered[$_-1]+1 == $ordered[$_] } 1..$#ordered;
}

printf "%.1f%%\n", $has_adjacent_titles / $num_tests * 100;

输出:

>a.pl
38.6%

>a.pl
38.8%

>a.pl
38.5%

似乎shuffle 运行良好。


  1. Combinatorial restriction on choosing adjacent objects

【讨论】:

  • 感谢您提供全面而明智的回答。你的测试代码优雅简洁;正是我喜欢 Perl 的原因。
猜你喜欢
  • 2011-07-07
  • 1970-01-01
  • 2013-04-07
  • 2013-04-07
  • 2012-12-15
  • 2017-11-26
  • 1970-01-01
  • 2011-08-18
  • 1970-01-01
相关资源
最近更新 更多