概率采样问题
作者:jostree 转载请注明出处 https://www.cnblogs.com/jostree/p/10274908.html
从n个数中等概率选取1个数,n未知
如果元素总数是n,那么每个元素备选到的概率应该是1/n, 然而N只有遍历结束时才会知道,于是我们可以利用乘法公式凑出1/n:
\[p_i = \frac{1}{i} \times \frac{i}{i+1} \times \frac{i+1}{i+2} \times \dots \times \frac{n-1}{n} = \frac{1}{n}
\]
在从前向后遍历的过程中,不断的根据概率改变我们的候选元素,第i个元素被最终选中的概率计算公式恰好为上式。
可以从代码简单实现:
class RandomSelectOne
{
public:
int count;
int selected;
RandomSelectOne():count(0){}
void add(int num)
{
count++;
if(rand() % count == 0)
selected = num;
}
int getSelected()
{
return selected;
}
}
从n个数中等概率的选取m个数,n未知
我们只需要把selected扩展成一个数组即可,对于第i个元素,其被选择的概率值为:
\[p_i = \frac{m}{i} \times \frac{i}{i+1} \times \dots \times \frac{n-1}{n} \ \ \ \ i > m
\]
\[p_i = \frac{1}{1} \times \frac{m}{m+1} \times \frac{m+1}{m+2} \times \dots \times \frac{n-1}{n} \ \ \ \ i \leq m
\]
对于前m个数,直接加入候选,这个数出现在最终候选名单的概率为每次加入新的值时,该数都没有没选中。对于m以后的数,被选进来的概率为m/i,在以后每次加入新数时,都没有被选中,即可以有上式表示。
代码如下:
class RandomSelect
{
public:
int count;
int scount;
vector<int> selected;
RandomSelect(int m):count(0), scount(m){}
void add(int num)
{
count++;
if(selected.size() < scount)
{
selected.push_back(num);
}
else
{
int idx = rand() % count;
if( idx < scount )
{
selected[idx] = num;
}
}
}
vector<int> getSelected()
{
return selected;
}
};
带权的n个数随机选取一个数的问题,n未知
设元素总数为n,当然在遍历结束前n是未知的。设第 \(i(1 \leq i \leq n)\) 个元素的权重为 \(w_i\) ,则权重总和为\(w=\sum_{i=1}^n w_i\) ,也是在遍历结束时才知道的。根据题目要求,第\(i\)个元素被选取的概率应该等于\(pi=\frac{w_i}{w}\)。
如果最终被选择的是第i个元素,那么必须在遍历到它时,恰好被选中:
\[\frac{w_i}{w} = \frac{w_i}{\sum_{k=1}^i w_k}
\]
另外,在处理后面的元素时,第i个元素没有被替换掉,对于任意的\(j( i < j \leq n )\),第i个元素都不会被选中,其概率为:
\[\frac{w-w_j}{w} = \frac{\sum_{k = 1} ^ {j - 1} w_k}{\sum_{k = 1} ^ j w_k}
\]
从而第i个元素最终被选择的概率为:
\[p_i = \frac{w_i}{\sum_{k=1}^n w_i}
\]
代码如下:
class RandomSelectOne
{
public:
double totalWeight;
int selected;
RandomSelectOne():totalWeight(0.0){}
void add(int num, double weight)
{
totalWeight += weight;
if(rand() % 10000 / 10000.0 * totalWeight < weight)
selected = num;
}
int getSelected()
{
return selected;
}
};
带权的n个数随机选取m个数,n未知
当m=2时,第i个元素被选中有两种情况,第一次被选中,第一次未被选中,第二次被选中,从而可以得到:
\[p_i(2) = \frac{w_i}{w} + \sum_{j \neq i} (\frac{w_j}{w} \times \frac{w_i}{w-w_j})
\]
也可以计算\(\bar{p}\)表示不被选中的概率:
\[\bar{p}_i(2) = \sum_{j \neq i} (\frac{w_j}{w} \times \frac{w - w_j - w_i}{w - w_j})
\]
很容易得到\(p_i(2) + \bar{p}_i(2) = 1\)
当m>2时,元素i未被选中的概率为:
\[\bar{p}_i(m) = \sum_{j_1}(\frac{w_{j_1}}{w} \sum_{j_2} ( \frac{w_{j_2}}{w-w_{j_1}} \sum_{j_3} (\frac{w_{j_2}}{w-w_{j_1}-w_{j_2}} \dots \sum_{j_m} \frac{w_{j_m}}{w - \sum_{k=1}^{m-1} w_{j_k}})))
\]