概率采样问题

作者: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}}))) \]

posted @ 2019-01-15 23:49  jostree  阅读(704)  评论(0编辑  收藏  举报