水塘抽样

Posted by ysd on September 10, 2016

有N个元素的链表,事先不知道有多长,写一个函数可以高效地从其中取出k个随机数。

水塘抽样:水塘抽样是一系列的随机算法, 其目的在于从包含n个项目的集合S中选取k个样本,其中n为一很大或未知的数量, 尤其适用于不能把所有n个项目都存放到主内存的情况。

從S中抽取首k項放入「水塘」中                       
對於每一個S[j]項(j ≥ k):                       
   隨機產生一個範圍從0到j的整數r                        
   若 r < k 則把水塘中的第r項換成S[j]項   

下面证明每一项被抽取的概率均为 k/n:
对于首先放入水塘的k项,其在遍历结束后任然留在水塘中的概率,即对于每个 j ≥ k 的项,都不会替换它,为:
对于每个 j ≥ k 的项,其进入水塘,并且在遍历后留在水塘的概率为:

leetcode上的一道题:Linked List Random Node

Given a singly linked list, return a random node’s value from the linked list. Each node must have the same probability of being chosen.

class Solution {
public:
    /** @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)
        : head_(head) {
        
    }
    
    /** Returns a random node's value. */
    int getRandom() {
        auto node = head_;
        int index = 0;
        int ans = node->val;
        while (node = node->next) {
            int r = randab(0, ++index);
            if (r == 0) {
                ans = node->val;
            }
        }
        return ans;
    }
private:
    int randab(int a, int b) {
        return (rand() % (b-a+1))+ a;
    }
    ListNode* head_;
};