加权随机数生成

时间:2010-11-17 18:00:10

标签: c random

我想以精确的方式生成加权随机数。我可以用一个例子来解释:我的输入数组是[1,2,3],它们的权重再次是[1,2,3]。在那种情况下,我希望看到1次1次,2次2次,3次3次。比如3 - > 2 - > 3 - > 1 - > 3 - > 2 ...

我使用rand()实现随机数生成,以获得[0,sum_of_weights]之间的范围。对于上面的示例,sum_of_weights = 1 + 2 + 3 = 6。我在互联网上搜索现有的解决方案,但结果不是我想要的。有时我在序列中得到2次以上且没有1次。它仍然加权但不完全给出我等待的次数。

我不确定下面的代码有什么问题。我应该做错事还是我尝试完全不同?谢谢你的回答。

int random_t (int items[], int items_weight[], int number_of_items)  
{   
    double random_weight;  
    double sum_of_weight = 0;
    int i;

    /* Calculate the sum of weights */  
    for (i = 0; i < number_of_items; i++) {
        sum_of_weight += items_weight[i];
    }

    /* Choose a random number in the range [0,1) */
    srand(time(NULL));
    double g = rand() / ( (double) RAND_MAX + 1.0 );
    random_weight = g * sum_of_weight;

    /* Find a random number wrt its weight */
    int temp_total = 0;

    for (i = 0; i < number_of_items; i++) 
    {
            temp_total += items_weight[i];

            if (random_weight < temp_total)
            {
                return items[i];
            } 
    }   
        return -1; /* Oops, we could not find a random number */
}

我也尝试了不同的东西(代码如下)。它适用于我的情况,但整数溢出和广泛使用静态变量使它成为问题。

如果在给出NULL之前输入输入数组并继续使用它。有点类似于strtok()用法。

int random_w(int *arr, int weights[], int size)
{
    int selected, i;
    int totalWeight;
    double ratio;
    static long int total;
    static long int *eachTotal = NULL;
    static int *local_arr = NULL;
    static double *weight = NULL;

    if (arr != NULL) 
        {
            free(eachTotal);
            free(weight);
            eachTotal = (long int*) calloc(size, sizeof(long));
            weight = (double*) calloc(size, sizeof(double));
            total = 0;
            totalWeight = 0;
            local_arr = arr;

            for (i = 0; i < size; i++) 
            {
                totalWeight += weights[i];
            }

            for (i = 0; i < size; i++)
            {
                weight[i] = (double)weights[i] / totalWeight;
            }
            srand(time(NULL));
        }

    while (1)
    {
        selected = rand() % size;
        ratio = (double)(eachTotal[selected])/(double)(total+1);
        if (ratio < weight[selected])
        {
            total++;
            eachTotal[selected]++;

            return local_arr[selected];
        }
    }
}

5 个答案:

答案 0 :(得分:4)

这是你想要的吗?

# Weights: one 1, two 2s, three 3s
>>> import random
>>> vals = [1] * 1 + [2] * 2 + [3] * 3
>>> random.shuffle(vals)
>>> vals
[2, 3, 1, 2, 3, 3]

编辑:哎呀,出于某种原因,我的思想用Python替换了C标签。无论如何,我认为你想要的不是“加权”随机数发生器,而是一种洗牌。 This应该有所帮助。

答案 1 :(得分:1)

如果您说您没有“准确”获得每个加权值的预期值,那么您说的是多少次?如果您只进行了六次任何随机过程,我不希望您能够明确地说出任何有效的信息。您的代码可能正常工作。尝试运行它一百万次,然后检查结果。或者你可能真的想要Nathon所说的,一个预先加权的值列表,然后你可以随机地随机播放并且仍然具有你正在寻找的确切权重。

答案 2 :(得分:1)

您可以从multinomial distribution进行抽样。随机样本(或“桶中的球”)的范围为{1, 2, 3},观察每个样本的概率(“权重”)分别为{1/6, 2/6, 3/6}

出于演示目的,Perl脚本可以为您提供具有以下概率的标记球的观察列表:

#!/usr/bin/perl

use strict;
use warnings;
use Math::Random qw(random_multinomial);
use Data::Dumper;

my $events = 10;
my @probabilities = qw(0.167 0.333 0.5);
my @observations = random_multinomial($events, @probabilities);

print Dumper \@observations;

对于10个事件,单个试验将返回如下内容:

$VAR1 = 1;
$VAR2 = 2;
$VAR3 = 7;

这意味着您(在此单次试用中)有一个1标记的事件,两个2标记的事件和七个3标记的事件。

如果您重复试用,则可能会对123标记的事件进行不同的分发。

您可以轻松地将此列表构建到等效的{1, 2, 2, 3, 3, 3, 3, 3, 3, 3}列表中。

随机随机播放第二个列表,以获得加权的观察到的随机数列表。

答案 3 :(得分:0)

如果你想让样本频率完全确定,我想 要走的路是生成一个具有适当出现次数的数组 对于每个值,然后进行随机shuffle(保留频率) 并将混洗数组的连续元素作为随机序列。

答案 4 :(得分:0)

好吧,我的答案听起来像是一个黑客 - 但很短或写自己的发行版 - 也许你可以映射统一分布和杠杆增强(查看http://www.boost.org/doc/libs/1_44_0/doc/html/boost_random/reference.html#boost_random.reference.distributions

所以按照你的例子:

  • 1 - &gt; 1
  • 2,3 - &gt; 2
  • 4,5,6 - > 3
  • 7,8,9,10 - &gt; 4(等等......)

然后生成1到10之间的随机数并返回映射的元素。 然后使用boost的uniform_int分布来获取一个数字然后映射。

这是生成数字的示例;然后,您需要映射结果:

#include <iostream>
#include <boost/random.hpp>
#include <time.h>
using namespace std;
using namespace boost;

int main ( )  {

    uniform_int<> distribution(0, 10) ;
    mt19937 engine; 
    engine.seed(time(NULL));   
    variate_generator<mt19937, uniform_int<> > myrandom (engine, distribution);

    cout << myrandom() << endl;

}