[英]How to optimize rejection sampling
我有一個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?
如果要按比例線性偏移樣本,則很容易做到。
首先計算所有值的總和。
現在生成一個介於0和總和之間的隨機浮點值。
遍歷地圖,並在添加時對值求和。 當總和大於先前計算的隨機值時,您就已經找到了樣本。
如果要在不變的地圖上重復進行此操作,則可以創建一個和向量,並對隨機值進行二進制搜索。
拒絕采樣主要用於連續分布。 您需要采樣離散分布 。 幸運的是,這是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
向量keys
和prob
包含地圖的鍵和值(概率)。 這是因為std::discrete_distribution
僅考慮概率。
請注意, operator()
不能為const
因為std::discrete_distribution
會在每個樣本處自然改變狀態。
還要注意,即使您使用累積分布和二進制搜索來實現抽樣(抽樣是域大小的對數時間),也存在更有效的(恆定時間)抽樣方法,例如alias方法 。 我不確定std::discrete_distribution
使用什么方法。
一種可能是使用帶有未知壞鍵的第二個map
(或set
)(將所有鍵放在此處,一旦拒絕了某個鍵,因為它大於初始隨機變量,則從映射中將其刪除-然后您在未知壞集中而不是在整個地圖中搜索密鑰...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.