在解决一些问题时,往往需要一些随机数或者随机数组的输出,这时候就需要能够输出随机数的函数或者能打乱输入的值的顺序得到随机数列的函数或者说算法。
各个编程语言基本都有自己内置的的随机数生成函数,具体原理可以自行了解,只要会用就可以了,本文主要关注利用这个基本的随机数生成函数设计的一些随机算法的实现和利用这样的算法用程序解决生日悖论这样的问题。
1、随机排列数组的两种算法的实现
①一种方法是利用随机数生成函数对数组的每个元素重新设置一个序号,在新的数组中将这些数字按照生成的序号的顺序进行重新排序,达到随机排列数组的效果。
具体的python实现如下。
import random
def random_sort(list0):
n=len(list0)
list1=[]
for i in range(len(list0)):
k=random.randint(0,n-1)
list1.append(list0[k])
del(list0[k])
n=n-1
return list1
可以看出,这种算法对于长度为
n
n
的数组需要大小为的辅助空间,时间复杂度为
Θn
Θ
n
。
②另一种方法就是放弃另建一个数组,直接在原数组上对元素顺序进行随机调换。
具体的python实现如下。
def random_switch(list0):
n=len(list0)
for i in range(n):
k=random.randint(i,n-1)
swap=list0[i]
list0[i]=list0[k]
list0[k]=swap
return list0
可以看出,这种算法只需要大小为
1
1
的辅助空间,时间复杂度也为。
2、解决生日悖论问题
生日悖论问题:最少需要多少人就能使得其中两个人的生日相同的概率达到50%?
这个问题的答案其实要远远小于一年的的天数365,远小于这个数的一半,这个问题的答案是23。也就是说随机抽取23人就可以让其中有两人的生日相同的概率达到50%。
这个问题的数学解法这里就不介绍了,但我们在对数学概率知识不精通的情况下完全可以编写程序来得到23这个答案。
首先,利用程序解决这个问题的主要依据是大数原理,即当实验次数够多的时候,事件发生的频率的算数平均数会趋近理论概率,即最终收敛于理论上的概率。
其次,如何解决两人的生日相同的概率的表示问题。在一次实验中,我们可以将事件发生了表示为
1
1
<script type="math/tex" id="MathJax-Element-12">1</script>,事件未发生表示为0,最后对这些值进行累加并除以总的实验次数,即可得到发生的频率的算数平均值。
具体python实现代码如下。
#从list0中随机选出需要的n个人
def people_take(list0,n):
n0=len(list0)
list1=[]
#通过循环n次得到选出的n个人的数组list1
for i in range(n):
k=random.randint(0,n0-1)
list1.append(list0[k])
return list1
#对判定事件“有两个人的生日相同”事件是否发生
def random_count(list0):
n0=len(list0)
set0=set(list0)
n1=len(set0)
sum0=0
#如果有生日一样的人,则sum0=1,否则sum0=0
if n1<n0:
sum0=1
return sum0
#主函数,basic_number为基数,即生日可能的数量365,people_number为选出作为样本的人数
#circle_number为循环次数,即实验进行的次数
def find_number(basic_number,people_number,circle_number):
list_origin=[x for x in range(basic_number)]
people_result=0
dirc={}
for people in range(5,people_number):
sum=0
for circle in range(circle_number):
#将生日的数组乱序排列
list_origin=random_switch(list_origin)
#从打乱的生日数组中随机取出n个样本,双重随机保证样本数据的随机性
list_get=people_take(list_origin,people)
sum=sum+random_count(list_get)
#circle_number次实验后得出的时间发生频率的算数平均数result
result=sum/circle_number
#将样本人数和得出的有两人相同生日的概率存入字典,方便查看
dirc.update({people:result})
return dirc
最终以实验次数为10000次进行实验,得出如下的结果。
find_number(365,50,10000)
{5: 0.0286,
6: 0.0424,
7: 0.0578,
8: 0.0706,
9: 0.0963,
10: 0.1128,
11: 0.1438,
12: 0.1722,
13: 0.1991,
14: 0.2241,
15: 0.2477,
16: 0.2862,
17: 0.3111,
18: 0.3464,
19: 0.3779,
20: 0.4101,
21: 0.4452,
22: 0.4653,
23: 0.5036,
24: 0.5469,
25: 0.5714,
26: 0.6011,
27: 0.6242,
28: 0.6516,
29: 0.6773,
30: 0.7035,
31: 0.7282,
32: 0.7553,
33: 0.7739,
34: 0.7916,
35: 0.8217,
36: 0.8334,
37: 0.8558,
38: 0.8682,
39: 0.8751,
40: 0.8925,
41: 0.9023,
42: 0.9132,
43: 0.9261,
44: 0.9273,
45: 0.941,
46: 0.9451,
47: 0.951,
48: 0.9595,
49: 0.9615}
可以看出,当人数达到23人时,10000次的实验表明可以使其中两个人的生日相同的机会达到50%,而当人数达到48人时,有超过95%的可能性使得有两人的生日相同。