概率题汇总

51 篇文章 1 订阅
1 篇文章 0 订阅

1. shuffle

对于当前位置i,等概率地从[0,i](或者[i,n-1])选取一个数。

1)从[0,i]选取

循环不变式:当前数组[0,i]的每个数在位置[0,i]出现的概率是相等的

随机函数形式:rand(i), 产生等概率的生成[0,i]上的数

2)从[i,j]选取

循环不变式:原数组的每个数在位置[0,i]出现的概率是相等的

随机函数形式:rand(i, j), 等概率的产生[i,j]之间的数。

2. 从一个文本文件中随机读取一行(流中随机取一个数)

不变式:对于已读的行,每一行都是等概率的被保存在selectedLine里的

import random
def get_random_line (filename):
    index, selectedLine = 0, ''
    with open(filename) as f:
        for line in f.readlines():
            if random.randint(0, index) == 0:
                selectLine = line
            index += 1
    return selectLine

3 流随机抽样:一个流中随机选取k个数

先读k个数,之后每读一个数:

1)以k / n, (n为已读总数)的概率决定是否落到池子 if (randint(0, n - 1) < k)

2)随机取池中一个位置交换(其实是扔掉/覆盖,因为只关心数组的前k个数)

第二种更简单的理解及方法:

i为当前序号, 产生一个 [0, i]上的均匀的随机数 x,如果x < k,即落在[0, k-1]的池子里,则替换A[x]上的数。

和舍弃法的原理一样,如果随机变量等概率的落在一个区间[a, b],则一定等概率的落在其任意子区间。

vector<string> random_sample(ifstream &stream, int k) {
	vector<string> pool(k);
	string line;
	for(int i = 1; !stream.eof(); ++i) {
		stream >> line;
		int index = rand() % i;
		if (index < k) pool[index] = line;
	}
	return pool;
}

这是假设流的size远大于 k,否则有可能流读完了k个采样没取够,解决办法:重复这个过程,直到取够k个

vector<int> pick(vector<int> &A, int k) {
	vector<int> pool(k), marked(k);
	for (int count = 0; true; count = 0, marked = vector<int>(k)) {
		for (int i = 0; i < A.size(); ++i) {
			int index = rand() % (i + 1);
			if (index < k) {
				pool[index] = A[i];
				if (marked[index] == 0) { //only count add, not replacment
					marked[index] = 1;
					++count;
				}
			}
		}
		if (count == k) return pool;
	}
}


4. 给定函数rand7()可以随机产生[1, 7]之间的数,实现rand5()  可以随机产生[1,5]之间的数

舍弃法,调用rand7(),返回值大于5则舍弃,重新调用,直到返回值小于等于5

int rand7();
int rand5() {
    while (ture) {
        int r = rand7();
        if(r<=5) return r;
    } 
}

5. 给定函数rand5(), 实现rand7()

思路:先用rand5() 级联出一个范围[1,n](n>=7)上的随机函数,然后再用舍弃法。

int rand5(){
    return rand() % 5 + 1;
}
int rand7() {
    while (true) {
        int value = (rand5()-1) * 5 + rand5() -1;
        if(value < 21 ) return value % 7 + 1;
    } 
}

6. 给你一堆数,每个数有个weight, 按weight 的概率sample一个数

1)想象一个数轴,每个数的weight占数轴上一段,等概率的sample数轴,落在哪一段就等于sample到对应段上的数。前缀和 + 二分搜索

int getByWeightedRandom(vector<int> &A, vector<int> W) {
	for (int i = 1; i < W.size(); W[i] += W[i - 1], ++i);
	int w = rand() % W.back();
	int j = lower_bound(W.begin(), W.end(), w) - W.begin();
	if (W[j] == w) ++j; // the interval is like [a, b)
	return A[j];
}

2) 第二步,w落在哪个数的区间,也可以用反复sample法,随机选一个候选数,判断w是否落在这个数的weight区间,重复这个过程,直到满足条件。

int weighted_sample(vector<int> &A, vector<int> W) {
	for (int i = 1; i < W.size(); ++i) W[i] += W[i - 1];
	int w = rand() % W.back();
	for (;;) {
		int i = rand() % A.size();
		if (w < W[i] && (i == 0 || w >= W[i - 1])) return A[i];
	}
}





7. 用不均匀01随机数发生器构造均匀的01随机数发生器

int rand01NotEven();
int rand01Even() {
	while (true) {
		int x = rand01NotEven();
		int y = rand01NotEven();
		if (x != y) return x;
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值