如何优化拒绝采样

How to optimize rejection sampling

本文关键字:拒绝 采样 优化 何优化      更新时间:2023-10-16

我有一个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循环可能会循环多次,这在我的应用程序中造成了相当大的瓶颈

编辑

此外,我有必要对我的样本进行任何调整吗,因为它们在这里有偏差?我知道在蒙特卡罗积分中,你必须将样本的值除以该样本的PDF。。。但我不确定这是否适用于这里。如果它真的适用,我该如何找到PDF?

拒绝采样主要适用于连续分布。您需要的是对离散分布进行采样。幸运的是,这是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在每个样本处(自然地)改变状态。

还要注意,即使您使用累积分布和二进制搜索(其中采样是域大小的对数时间)自己实现采样,也有更有效的(恒定时间)采样方法,如别名方法。我不知道用什么方法std::discrete_distribution

如果你想让你的样本与数值成比例线性偏差,这很容易做到。

首先计算所有值的总和。

现在生成一个介于0和和之间的随机浮点值。

在地图上进行迭代,边进行边求和。当总和大于前面计算的随机值时,您就找到了您的样本。

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

一种可能性是使用第二个具有未知坏键的map(或set