部分随机算法之小结

本文探讨了如何实现特定概率分布的随机数生成,包括基于均匀分布的简单方法及利用已有随机函数生成不同概率分布的方法。此外,还讨论了如何通过rand5()生成rand7()的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇博客总结下sumnous 大神 博客的随机算法,每次大神英文名都会拼错= =

http://sumnous.github.io/blog/2014/05/13/random-pick-function/

如果给定了一个要在某些概率区间返回某数,例如 0.6概率生成2,0.3.概率生成1, 0.1 概率生成0,怎么实现。简单而言就是 输入[[0.6,2],[0.3,1],[0.1,0]]

其实一个简单做法就是用个均匀分布,举例,在前0.6的返回2,中间0.3的返回1,最后0.1的返回0。其实随便哪个0。6的部分都可以,甚至可以不连续,按照这个思路,有大神的python代码,大神是python流。

def withProbRandomPick(probList):
    import random
    import sys
    r, s = random.random(), 0
    for num in probList:
        s += num[1]
    	if s >= r:
    	    return num[0]
    print >> sys.stderr, "Error: shouldn't get here"

简单而言就是概率一直累加,累积到大于生成的随机数,就返回对应的返回值。感觉这个不够make sense,先写个最通俗版的

int f()
{
	while(1)
	{
		int x=rand()%10;
		if(0<=x<5)
			return  2;
		if(5<=x<8)
			return 1;
		if(8<=x<=9)
			return 0;
	}
}

这样可能看起来不够牛叉,但是比较接地气,一下子就看懂。但是这种写法不够通用,因为你不知道输入有多少个概率区间,于是乎就整合成上面的版本了。

这道是入门级的,现在看一道稍微challenge的题,利用一个随机函数(0.6概率返回1,0.4概率返回0),生成0.5概率返回1,0.5概率返回0的随机函数,当然不能用系统库

这道一眼反应就是凑一个概率等于0.5,由于是两点分布,我想到用二项分布,然后将多个基本事件加起来等于0.5,也即凑一个概率和等于0.5,但是发现试验次数N=2,3,4,5一直下去,小数点越来越多,几乎不太像能凑到0.5,难道无限多有个极限是0.5?我也没想那么多,sumous大神给出的答案让我目瞪口呆,直接两项把00 11的结果丢弃。。。我思维太狭窄了,不够开阔,这样显然 01 10的概率都是0.24了,于是乎代码如下:

def g():
    while(true):
        a = f()
        b = f()
        if [a,b] == [0,1]:
            return 0
        if [a,b] == [1,0]:
            return 1
另外再附上一道最近一直想的题目,由这道题目可以归纳出一个稍微general的规则出来。由rand5() 生成rand7(),randi()是生成i个数的随机函数.这道题目在Lucida大神称为翔一样书的程序员面试宝典里面也作为例子,思路就是 x=5*rand5()+rand5() 这样可以产生一个更大的数,然后模7,就可以生成均匀概率的7个数了。随机函数一定是生成输出空间里每个数的概率都一样的,不然怎么称为随机,尽管计算机都是伪随机数。简单说明就是5*[0,4]+[0,4]=[0,24] 其实也就是生成了5^2个点,然而这里每个点都只有一个 两次生成的数字对应,并且连续的从0,24,不会漏数,这样有个极大的好处:第一这样保证每个点的概率都一直,1/25,举个例子,如果用4*rand5()+rand5()的话,生成4就有[1,0] [0,4]两种实验结果,不确保概率均一性 第二中间不会遗漏数,这样到后面可以方便取模,最后模拟之后根据7的值丢弃掉一些多出的部分,x%21 x%14 x%7应该都是可以的,只是%21会使丢弃的最少,这样可以更快的产生需要的数字,%14可能需要多次的循环。所以总结就是randi 就用x=i*randi()+randi(), 这样将结果空间等概率的投射到i^2的连续空间,只要7<i^2, 原则上都可以这么做,附上大神代码:

def rand7():
    while(true):
        num = 5 * rand5() + rand5()
        if num < 21:
            return num % 7
最后一道思考题,如果rand7()->rand5(),感觉是一种把戏,

def rand5():
    while(true):
        num = 7 * rand7() + rand7()
        if num < 45:
            return num % 5
包括July大神博客总结的rand7()->rand10(),应该也是一样的技巧。

经过大量随机实验测试,结果比较接近理论分析值

#include <stdio.h>
#include <stdlib.h>
#include <string>
int rand5()
{
	return rand()%5;
}
int rand7()
{
	
	while(1)
	{
		int x=5*rand5()+rand5();
		if(x<21)
			return  x%7;
	}
}
int main()
{
	int Percent1Count=0;
	const int RandomNum=7;
	int count[RandomNum];
	memset(count,0,sizeof(int)*RandomNum);
	for(int i=1;i<=10000000;i++)
	{
		int x=rand7();
		count[x]++;
	}
	
	for(int i=0;i<RandomNum;i++)
	{
		printf("%d: %d\n",i,count[i]);
	}
	return 0;
}

另外最近看到July大神博客

http://blog.youkuaiyun.com/v_july_v/article/details/6419466

总结的求数组几个数和为给定值,二分方法的时间复杂度为O(nlogn) 我就有点怀疑了,因为时间复杂度是上界的最小值,是否不够渐进,因为操作次数是log(n-1)+...log1, 想到mathmatics for computer science(MIT和Google联合出的,之前微博看到,里面递归的部分特别喜欢) 一书给出的单调不减函数求和式上下界的一个不等式,遂用,发现上界还是O(nlogn), 我猜July大神应该是直觉上估计的,因为n次loop,每次logn 但是这个n会逐渐变到1,所以我有点怀疑,不过结论还是打消了疑虑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值