如何从具有非均匀概率的列表中选择值?

时间:2011-12-19 22:07:15

标签: c++ probability

我在查看k-means++初始化算法。算法的以下两个步骤会产生不一致的概率:

  

对于每个数据点x,计算D(x),x和x之间的距离   最近的中心已被选中。

     

使用加权随机选择一个新数据点作为新中心   概率分布,其中以概率选择点x   与D(x)^ 2成比例。

如何在C ++中用这个陈述的加权概率分布进行选择?

4 个答案:

答案 0 :(得分:5)

使用random标头并使用std::discrete_distribution C ++ 11 中更容易做离散分布。这是一个例子:

#include <iostream>
#include <map>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d({20,30,40,10});
    std::map<int, int> m;
    for(int n=0; n<10000; ++n) {
        ++m[d(gen)];
    }
    for(auto p : m) {
        std::cout << p.first << " generated " << p.second << " times\n";
    }
}

这是输出的一个示例:

0 generated 2003 times
1 generated 3014 times
2 generated 4021 times
3 generated 962 times

答案 1 :(得分:3)

对于一组有限的单个数据点X,这需要离散的概率分布。

最简单的方法是按顺序枚举点X,并计算表示其累积概率分布函数的数组:(伪代码如下)

/* 
 * xset is an array of points X,
 * cdf is a preallocated array of the same size
 */
function prepare_cdf(X[] xset, float[] cdf)
{
   float S = 0;
   int N = sizeof(xset);
   for i = 0:N-1
   {
      float weight = /* calculate D(xset[i])^2 here */
      // create cumulative sums and write to the element in cdf array
      S += weight;
      cdf[i] = S;
   }

   // now normalize so the CDF runs from 0 to 1
   for i = 0:N-1
   {
      cdf[i] /= S;
   }
}

function select_point(X[] xset, float[] cdf, Randomizer r)
{
   // generate a random floating point number from a 
   // uniform distribution from 0 to 1
   float p = r.nextFloatUniformPDF();
   int i = binarySearch(cdf, p);
   // find the lowest index i such that p < cdf[i]

   return xset[i];
}

您只需调用prepare_cdf一次,然后根据需要多次调用select_point来生成随机点。

答案 2 :(得分:1)

我采取以下方法:

  • 迭代数据点,将其D平方存储在double distance_squareds[]std::vector<double> distance_squareds或其他内容中,并将其D平方的总和存储在double sum_distance_squareds中。
  • 使用the drand48 function在[0.0,1.0)中选择一个随机数,然后乘以sum_distance_squareds;将结果存储在random_number
  • 迭代distance_squareds,将值(再次)加在一起,一旦运行总数达到或超过random_number,就返回对应于您所在的D平方的数据点刚补充。
  • 由于四舍五入错误,你很可能在没有返回的情况下完成循环;如果是这样,只需返回第一个数据点,或最后一个数据点,或其他任何数据点。 (但不要担心,这应该是非常罕见的边缘情况。)

答案 3 :(得分:0)

这里有一些可以帮到你的东西, 使用(数字..)数组给定概率分布(概率..)它将为你(数字)生成这些概率(这里它将计算它们)。

#include <iostream>
#include <cmath>
#include <time.h>
#include <stdlib.h>
#include <map>
#include <vector>
using namespace std;
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))

int checkDistribution(double random, const map<double, vector<int> > &distribution_map)
{
    int index = 0;
    map<double, vector<int> >::const_iterator it = distribution_map.begin();
    for (; it!=distribution_map.end(); ++it)
    {
        if (random < (*it).first)
        {
                int randomInternal = 0;
                if ((*it).second.size() > 1)
                    randomInternal = rand() % ((*it).second.size());
                index = (*it).second.at(randomInternal);
                break;
        }
    }
    return index;
}

void nextNum(int* results, const map<double, vector<int> > &distribution_map)
{
    double random  = (double) rand()/RAND_MAX;
    int index = checkDistribution(random,distribution_map);
    results[index]+=1;
}

int main() {

    srand (time(NULL));
    int results [] = {0,0,0,0,0};
    int numbers [] = {-1,0,1,2,3};
    double prob [] =  {0.01, 0.3, 0.58, 0.1, 0.01};
    int size = ARRAY_SIZE(numbers);
    // Building Distribution
    map<double, vector<int> > distribution_map;
    map<double, vector<int> >::iterator it;
    for (int i = 0; i < size; i++)
    {
        it = distribution_map.find(prob[i]);
        if (it!=distribution_map.end())
            it->second.push_back(i);
        else
        {
            vector<int> vec;
            vec.push_back(i);
            distribution_map[prob[i]] = vec;
        }
    }
    // PDF to CDF transform
    map<double, vector<int> > cumulative_distribution_map;
    map<double, vector<int> >::iterator iter_cumulative;
    double cumulative_distribution = 0.0;
    for (it=distribution_map.begin();it!=distribution_map.end();++it)
    {
        cumulative_distribution += ((*it).second.size() * (*it).first);
        cumulative_distribution_map[cumulative_distribution] = (*it).second;
    }

    for (int i = 0; i<100; i++)
    {
        nextNum(results, cumulative_distribution_map);
    }
    for (int j = 0; j<size; j++)
        cout<<" "<<results[j]<<" ";
    return 0;
}