如何优化拒绝采样

时间:2014-04-09 22:43:53

标签: c++ optimization map sample random-sample

我有一个std :: map mymap,我试图根据每个键的值进行采样。我已经建立了一个基于拒绝采样的算法,该算法似乎有效,但它非常慢(这个算法在我的程序中被调用了数千次)。

所以我想知道这是否是最好的方法,或者是否有更快/更高效的东西,而不是我可以做的。

以下是我目前的情况:

std::map<int, float> mymap; //My map that I am sampling

//These three floats are precomputed
int minKey;  //Min key in the map.  
int maxKey;  //Max key in the map.  
float maxValue; //Max value in the map.  

float x1, x2; //Two random variables;
int key;
float value;
do 
{
    x1 = (float)rand()/(float)RAND_MAX;
    x2 = maxValue * (float)rand()/(float)RAND_MAX;
    key = minKey*(1.0-x1) + maxKey*x1; //Linearly interpolate random value to get key;
    value = mymap[key]; //Get value;
} while(x2 > value) 


return std::pair<int, float)(key, value);

^所以我上面所做的是统一随机选择一个键。然后创建另一个随机变量并将其与该键的值进行比较。如果它更大,请重复此过程。这样,具有较高值的​​键比具有较低值的键更经常采样。但是,do-while循环可以循环多次,然后找到一个可接受的键值对来进行采样,这在我的应用程序中造成了相当大的瓶颈

修改

此外,我是否有必要对我的样品进行任何调整,因为它们偏向于此处?我知道在monte carlo集成中,你必须将样本的值除以该样本的PDF ...但我不确定这是否适用于此。如果它确实适用,我将如何找到PDF?

3 个答案:

答案 0 :(得分:2)

如果您想要将样本与数值成比例地线性偏置,那么这很容易做到。

首先计算所有值的总和。

现在生成0和总和之间的单个随机浮点值。

遍历地图,随时对值进行求和。当总和大于先前计算的随机值时,您已找到样品。

如果你要在一个不变的地图上重复这样做,你可以创建一个总和矢量并对随机值进行二分搜索。

答案 1 :(得分:2)

拒绝抽样主要用于连续分发。你需要的是sample a discrete distribution。幸运的是,这是C ++ 11中STL的一部分。因此,改编自std::discrete_distribution的样本:

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

template <typename T>
class sampler
{
    std::vector<T> keys;
    std::discrete_distribution<T> distr;

public:
    sampler(const std::vector<T>& keys, const std::vector<float>& prob) :
        keys(keys), distr(prob.begin(), prob.end()) { }

    T operator()()
    {
        static std::random_device rd;
        static std::mt19937 gen(rd());
        return keys[distr(gen)];
    }
};

int main()
{
    using T = int;
    sampler<T> samp({19, 54, 192, 732}, {.1, .2, .4, .3});
    std::map<T, size_t> hist;

    for (size_t n = 0; n < 10000; ++n)
        ++hist[samp()];

    for (auto i: hist)
    {
        std::cout << i.first << " generated " <<
        i.second << " times" << std::endl;
    }
}

输出:

19 generated 1010 times
54 generated 2028 times
192 generated 3957 times
732 generated 3005 times

向量keysprob分别包含地图的键和值(概率)。这是因为std::discrete_distribution仅考虑了概率。

请注意,operator()不能为const,因为std::discrete_distribution会自动更改每个样本的状态。

另请注意,即使您使用累积分布和二分搜索自己实施抽样(抽样是您域中大小的对数时间),也有更有效(恒定时间)的抽样方法,如{{3} }。我不确定使用什么方法 但是,std::discrete_distribution

答案 2 :(得分:0)

一种可能性是使用第二个map(或set)与未知 - 坏键(您将所有键放在那里,并且一旦拒绝了键,因为它是&#39; s大于初始随机变量,你将其从地图中删除 - 你在未知 - 坏集合中搜索密钥,而不是在整个地图中...