蓄水池采样问题(Reservoir sampling question)

问题: 在给定的但未知其大小的数据集中,随机等概率的抽取一个元素。
分析: 如果知道数据集的大小,可以直接rand() % len得到一个确切的位置。
所以未知长度时,即可使用蓄水池采样。
算法思路: 总是选择第一个对象,以\frac{1}{2}的概率选中第二个数据,依次类推... 以\frac{1}{m}的概率选中第m个数据。此时可以保证被选中的概率为\frac{1}{n}

第m个被选中,那么就是说后面的都不被选中(第m个数据不被选中,前m - 1个随机选中一个)
证明如下: P(m) = \frac{1}m * \frac{m}{m+1} * \frac{m+1}{m+2} * ... *\frac{n-1}n = \frac{1}n

 

以上是两个典型的问题(来源: leetcode): 
问题1(leetcode 382):
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。进阶:如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    //又是抽样问题???
    
    ListNode* H;

    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    Solution(ListNode* head){
        H = head;
    }
    
    /** Returns a random node's value. */
    int getRandom() { //随机返回一个数
        
        ListNode* p = H->next;
        int i = 1, res = H->val; //i 从第一个数开始
        while(p){
            int r = rand() % (i + 1);
            
            //这一步是为什么? 求解r == 0 即记录结果 总是先选择第一个数
            if(r < 1) res = p->val;
            p = p->next;
            i ++;
        }
        return res;
    } 
};

问题2(leetcode 398): 
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。注意:数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。

class Solution {
public:
    
    //随机+“大数组” --> 蓄水池采样
    vector<int> v; //定义二维map 以及怎么使用
    Solution(vector<int>& nums) {
        
        v = nums;
    }
    
    int pick(int target) {
        
        int res = 0, cnt = 0; //统计个数
        for(int i = 0; i < v.size(); i ++){ //随机给出其目标值的下标
            if(v[i] == target){
               cnt ++; //统计目标值的个数
                
                if(rand() % cnt == 0) res = i;
            } 
        }
        
        return res;
    }
};


拓展: 如何从大数据中随机抽取k个数字。
若是将前k个数字放进“水库”中去,对于第k+1个数据元素,以\frac{k}{k + 1}的概率选取它,以\frac{k + 1}{k + 2}的概率选取第k+2个数据元素...
依次类推,若第m个数据元素被选中\frac{k}{m} (说明:此时不被选中的概率为1- \frac{k}{m}   =  \frac{m - k}{m},则随机替换水库中的一个对象。最终每个对象被选中的概率为k/n。

证明如下: 第m个数据元素被选中 = 选择m的概率 * (其后元素不被选择的概率 + 其后数据元素被选中的概率*不替换第m个的概率)
P(m) = \frac{k}m * (\frac{m + 1 - k}{m +1} + \frac{k}{m +1}*\frac{k - 1}{k}) * ... *(\frac{n - k}{n} + \frac{k}{n}*\frac{k - 1}{k})

           =\frac{k}{m}*\frac{m}{n} =\frac{k}{n}

 

以上就是今天所有的内容,欢迎各位给出意见,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值