如何从Perl中的数组中获取加权随机选择?

时间:2009-12-31 07:04:40

标签: perl random

我需要从数组中随机选择一些元素。我通过随机化索引$array[int(rand(100))]来做到这一点。我希望更频繁地出现一些元素。我该怎么做?

我想到了一个愚蠢的解决方案,在阵列中多次重复这些元素,但我相信你们可以做得更好。

7 个答案:

答案 0 :(得分:5)

您想要生成weighted random sample。链接的问题包括有无替换。

答案 1 :(得分:3)

似乎一种相当自然的方式涉及设置二进制搜索。假设一群人参加抽奖活动,允许人们根据自己的意愿多次提交姓名。我们有以下名称,提交的数量如下:

  • 朱丽叶:2
  • Jenny:11
  • 杰西卡:7
  • Jan:1
  • Jane:1
  • Jean:5

现在,如果我们想从包中随机选择一个名称,我们只需为每个名称指定一个从0开始的范围:

  • 朱丽叶:0,1
  • Jenny:2,12
  • 杰西卡:13,19
  • Jan:20,20
  • Jane:21,21
  • Jean:22,26

好吧,我们有一个相邻范围的数组,每个范围都在0到26之间。我们使用修改后的二进制搜索来找到我们的目标项(伪代码):

let raffle := { Juliet: 0, 1;
                Jenny: 2, 12;
                Jessica: 13, 19;
                Jan: 20, 20;
                Jane: 21, 21;
                Jean: 22, 26 }

let search minIndex maxIndex rangeValue =
    if minIndex > maxIndex then
        failwith "Not found"

    let selectedIndex = (minIndex + maxIndex) / 2
    let item = raffle[selectedIndex]

    if item.range.min >= rangeValue && item.range.max <= rangeValue
        return item.name
    elif item.range.min < rangeValue
        return search minIndex (selectedIndex - 1) rangeValue
    else
        return search (selectedIndex + 1) maxIndex rangeValue

答案 2 :(得分:1)

This page提供了从任意分布生成随机数的理论。

答案 3 :(得分:0)

如果您知道要显示随机数的频率,则可以使用rand()函数。假设你希望数字0出现在33%的时间里,1出现在另外66%的出现时间。然后你会检查rand()&lt; 0.33,并返回一些索引。否则,返回另一个索引。这只是一种方法。

答案 4 :(得分:0)

另一个选择是有一个类似于以下的结构:(原谅我的语言,我实际上并不了解Perl)

[
  (1, 10),
  (2, 50),
  (3, 80),
  (4, 100)
]

然后当你从int(rand(100))得到值时,你可以依次将它与每个第二个元素进行比较并返回第一个元素。

答案 5 :(得分:0)

最通用的解决方案是一个充当(反向)cumulative distribution function的函数:一个函数,它从0.0到1.0的(均匀)分布映射到你想要的任何分布。

朱丽叶给出了一个很好的方法来实现其中之一。

答案 6 :(得分:0)

这个子'得到一个元素和权重数组-A(10)B(30)C(5) - 以及根据权重随机的一个元素。

sub we_rand {

my $line=$_[0];#get the elements array
my @b = split(/\(\d+\)/,$line);#b now holds each elemnet from the array
my @a = "";
my $i=0;
my $tmp;
    while ($line=~m/(\(\d+\)+)/g) {
        $tmp=$1;#temp gets the weight
        if ($tmp=~m/\d+/g) {
            if ($i>0){
            $a[$i]=$&+$a[$i-1];#if weight is grather then 0 -each cell of
            #a sums up the wheights up to it 
            }
            else {
            $a[$i]=$&;
            }
        }
        $i++;
         }
        if ($i>1){
            my $n=int(rand($a[$i-1])+1);#rand a number in the boundries of
            #the total weight of all the elements
            my $s=scalar(@b);
            #go through a and compare to the randomized num-then take the
            #element from b
            for ( $i=0;$i<scalar(@b);$i++){
                if($n<=$a[$i])
                {
                    $c=$b[$i];
                last;
                    }
            }
        } else {
        $c=$b[0];#if only one element
        }

        return $c;

}

你像这样称呼子'

my $ rand = we_rand(A(10)B(30)C(5)D(17)); - #$兰特= B