概率相关问题2

假设函数f()等概率随机返回一个在[0,1)范围上的浮点数,那么我们知道,在[0,x)区间上的数出现的概率为x(0

class RandomSeg {
public:
    // 等概率返回[0,1)
    double f() {
        return rand() * 1.0 / RAND_MAX;
    }
    // 通过调用f()来实现
    double random(int k, double x) {
        if(k<1)
            return 0;
        vector<double> vec;
        while(k--)
            vec.push_back(f());
        double max=-1.0;
        for(auto c:vec)
            {
            if(c>max)
                max=c;
        }
        return max;
    }
};

给定一个长度为N且没有重复元素的数组arr和一个整数M,实现函数等概率随机打印arr中的M个数。
每次随机选择一个序号,之后将这个序号的数放到最后一个,再从前面随机选一个。

class RandomPrint {
public:
    vector<int> print(vector<int> arr, int N, int M) {
        vector<int> res;
        for(int i=0;i!=M;++i){
        int num = rand()%(N-i);
        res.push_back(arr[num]);
        swap(arr[num],arr[N-1-i]);
        }
        return res;
    }
        void swap(int &a,int &b)
            {
            int temp;
            temp=a;
            a=b;
            b=temp;
        }
};

有一个机器按自然数序列的方式吐出球,1号球,2号球,3号球等等。你有一个袋子,袋子里最多只能装下K个球,并且除袋子以外,你没有更多的空间,一个球一旦扔掉,就再也不可拿回。设计一种选择方式,使得当机器吐出第N号球的时候,你袋子中的球数是K个,同时可以保证从1号球到N号球中的每一个,被选进袋子的概率都是K/N。举一个更具体的例子,有一个只能装下10个球的袋子,当吐出100个球时,袋子里有10 球,并且1~100号中的每一个球被选中的概率都是10/100。然后继续吐球,当吐出1000个球时,袋子里有 10 个球,并且1~1000号中的每一个球被选中的概率都是10/1000。继续吐球,当吐出i个球时,袋子里有10个球,并且1~i号中的每一个球被选中的概率都是10/i。也就是随着N的变化,1~N号球被选中的概率动态变化成k/N。请将吐出第N个球时袋子中的球的编号返回。

这个题目是经典的蓄水池算法,有很多的应用,比如在不知道总量的情况下随机选取某一行文档等。

1、处理1~k号球时,直接放进袋子里
2、处理第i号球时,以k/i的概率决定是否将第i号球放进袋子,如果不决定将第i号球放进袋子,直接扔掉第i号球,否者将袋子里的球随机扔掉一个,将i号球放入袋子
3、处理第i+1号球时,重复步骤1和步骤2.

证明过程:
假设第i号球被选中,并且i大于1小于k,在选k+1号球之前,第i号球留在袋子中的概率为1,在选k+1号球时,只有决定将k+1号球放入袋子,同时在袋子中的第i号球被随机选中并决定仍掉,这两个事件同时发生时第i号球才会被淘汰,第i号球被淘汰的概率是(k/(k+1))*(1/k)等于1/(k+1)所以第i号球留下来的概率是是k/(k+1);
依次类推,在选N号球时,从1号到N号的全部过程中,第i号球最终留在袋子中的概率是打搅看到的式子,化简之后为k/N。

k/(k+1)(k+1)/(k+2)(k+2)/(k+3)(k+3)/(k+4)....N1/N=k/N

当i>k时,再选i号球时,i号球选进袋子中的概率是k/i;在选第i+1号球时,只有决定将i+1放入袋子,并且决定扔掉i号球时,i号球才被淘汰,概率为(k/(i+1))(1/k),所以i号球留下来的概率是i/(i+1).从i号球被选中到第i+1号球的过程中,i号球留在袋子中的概率是(i/k)(i/(i+1).
和上面的分析过程类似,每一次处理球的过程中,第i号球都会有被淘汰的可能,当然也能算出被留下来的概率,当处理完N号球的时候,第i号球仍然没有被淘汰的概率依然为k/N。所以方法有效

k/(i)(i)/(i+1)(i+1)/(i+2)(I+2)/(i+3)....N1/N=k/N

代码如下:

class Bag {
public:
    vector<int> ret;
    // 每次拿一个球都会调用这个函数,N表示第i次调用
    vector<int> carryBalls(int N, int k) {
        if(N<=k)
            ret.push_back(N);
        else{
        double a=rand()%N;
        if(a<k)
           {
            ret[a]=N;
        }
        }
           return ret;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值