蓄水池采样算法

一般来说,如果从1000个数字里随机抽取100个数字的算法是很简单的,比如通过随机算法从[0,1000)中随机抽取100个数即可.但是当我们遇到数字总量未知的时候,就需要采用蓄水池采样算法.

蓄水池采样算法

假设数据序列的规模为 n,需要采样的数量的为 k。

首先构建一个可容纳 k 个元素的数组,将序列的前 k 个元素放入数组中。

然后从第 k+1 个元素开始,以 k/n 的概率来决定该元素是否被替换到数组中(数组中的元素被替换的概率是相同的)。 当遍历完所有元素之后,数组中剩下的元素即为所需采取的样本。

证明过程

对于第 i个数(i<=k),i被选中的概率为1,遍历到第k+1个数时,第i个数被替换的概率=第i+1个数被选中的概率+第i个数被替换的概率,即k/(k+1)*1/k=1/(k+1),那么被保留的概率= 1 - 1/(k+1)=k/(k+1),同理遍历到第k+2个数时有第i个数被保留的概率为1-k/(k+2)*1/k=1-1/(k+2)=(k+1)/(k+2)........

综上对于第i个数被选中的概率为: 1*k/(k+1)*(k+1)/(k+2)....*(n-1)*n = k/n;

对于第j个数(j>k),j被选中的概率为 k/j,不被j+1替换的概率为1-k/(j+1)*1/k=j/(j+1).....

综上对于第j个数被选中的概率为:k/j*j/(j+1)*(j+1)/(j+2)*...*(n-1)/(n) = k/n;

举个栗子

给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。

进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?

示例:

// 初始化一个单链表 [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);

// getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。
solution.getRandom();

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-random-node
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析题目可知链表的长度即为n,而k=1.

//第一步初始化数组,并将链表的头部值放入数组

    int res = head.val();
    head = head.next();
    //第二步,遍历单链表,并保证每次替换res值的概率为 k/n,即 1/i;
    //所以我们初始化i=2
    int i = 2;
    Random r = new Random();
    while(head!=null){
        if(r.nextInt(i)==0){//概率为1/i
            res = head.val();
        }
        i++;
        head = head.next();

    }
    return res;//返回结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值