当前位置: 首页 > 科技观察

我们在程序员节组织了一场比赛,竟然用Python来验证它的公平性?

时间:2023-03-22 12:46:01 科技观察

本文转载自微信公众号《数据与云》,作者杨宝。转载本文请联系数据和云公众号。程序员节那天,公司举行了抽奖活动。方法是将骰子掷六次,形成一个六位数,然后对本组人数取模。计算的结果就是获胜者的人数。但这种做法公平吗?让我们用Python来验证一下。1、验证骰子被掷了六次,那么这个值在111111到666666之间,有6个6次方(即46656)的随机数。nums=[xforxinrange(111111,666667)ifnotset(str(x)).intersection('0789')]print(len(nums))#46656假设群里有134人,用上面的46656个数取134的模数,查看最终结果分布。total_person=134nums_mod=list(map(lambdax:x%total_person,nums))foriinrange(0,total_person):print('人数:{},中奖次数:{}'.format(i,nums_mod.count(i)))中奖次数:0中奖次数:349中奖次数:1中奖次数:348中奖次数:2中奖次数:348中奖次数:3中奖次数:350中奖次数:4中奖次数:350中奖次数:5中奖次数:346人数:6,中奖人数:346人数:7,中奖人数:342人数:8,中奖人数:342人数:9,中奖人数:349人数:10,中奖人数:349...这是看数字不直观,我们把它翻译成图片:importmatplotlib.pyplotaspltx=range(0,total_person)y=[nums_mod.count(i)foriinx]fig,ax=plt.subplots()ax.plot(x,y)ax.set(xlabel='personno.',ylabel='prizecounts',title='{}person'.format(total_person))ax.set_xlim(0,total_person)ax.set_ylim(0,1000)plt.show()可以看到该组有134人,还是很公平的。假设再加入一个人,变成135人。每个人中奖的几率是多少?将total_person改为135,然后运行如下程序:number:0,winningtimes:280Number:1,winningtimes:577Number:2,winningtimes:297Number:3,winningtimes:297Number:4,winningtimes:297Number:5,中奖次数:297Number:6,中奖次数:581Number:7,中奖次数:284number:8,中奖次数:284number:9,中奖次数:284...这不公平,最少的人数wins是277,最大的是584,wins的个数比较多对应的序号以1和6结尾,为什么会这样呢?改造之前的代码。total_person=135fromcollectionsimportdefaultdictforiinrange(0,total_person):nums_filter=list(过滤器(lambdax:x%total_person==i,nums))num_last_number=defaultdict(int)forjinnums_filter:num_last_number[j%10]+=1print('number:{},中奖次数:{},骰子尾数统计:{}'.format(i,len(nums_filter),num_last_number))可以看到当数字尾数为1或6时,对应的骰子尾数是1或6,而其他的骰子末尾对应的骰子点数只有一个点数,即0-5、2-2、7-2等。这就是为什么以1和6结尾的中奖号码的数量接近于其他号码的两倍。数字:0,获胜次数:280,骰子尾数统计:defaultdict(,{5:280})数字:1,获胜次数:577,骰子尾数统计:defaultdict(,{1:297,6:280})number:2,winningtimes:297,dicemantissastatistics:defaultdict(,{2:297})number:3,winningtimes:297,骰子尾数统计:defaultdict(,{3:297})number:4,中奖次数:297,骰子尾数统计:defaultdict(,{4:297})number:5,中奖次数:297,骰子尾数统计:defaultdict(,{5:297})数:6,中奖次数:581,骰子尾数统计:defaultdict(,{1:284,6:297})数字:7,获胜次数:284,骰子尾数统计:defaultdict(,{2:284})数字:8,获胜次数:284,骰子尾数统计:defaultdict(,{3:284})number:9,winningnumber:284,dicemantissastatistics:defaultdict(,{4:284})...2.中提到的方法之前的breaking概述游戏人数为135是不公平的,如何保证公平。公平意味着每个人都有相同的获胜机会。这就要求骰子掷出的数字足够随机和连续。由于单个骰子只有6个不同的值,为了使其连续,我们将骰子的1-6分别对应数字0-5,并使用十六进制。比如骰子掷出的数字是1、3、4、6、2、5,那么对应的数字就是0、2、3、5、1、4,换算成十进制就是int('023514',6)=3430,代码可以这样替换:nums=[int(str(x),6)forxinrange(0,555556)ifnotset(str(x)).intersection('6789')]print(len(nums))total_person=135nums_mod=list(map(lambdax:x%total_person,nums))foriinrange(0,total_person):print('人数:{},中奖次数:{}'.format(i,nums_mod.count(i)))importmatplotlib.pyplotaspltx=range(0,total_person)y=[nums_mod.count(i)foriinx]fig,ax=plt.subplots()ax.plot(x,y)ax.set(xlabel='personno.',ylabel='prizecounts',title='{}person'.format(total_person))ax.set_xlim(0,total_person)ax.set_ylim(0,1000)plt.show()就是这样!自己的一个小游戏,主要想介绍一下Python中的map和filter函数,以及matplotlib绘图模块。最后附上小游戏代码。fromcollectionsimportdefaultdictclassPrize:DICE_MAX_DIGIT=5#骰子的最大点数,1-6的骰子,对应数字0-5def__init__(self,person_nums):#活动数self.person_nums=person_nums#中奖概率的差异,这里控制为1%self.percent_diff=0.01def_need_throw_times(self):"""确定需要抛出的次数"""self.throw_time=1#初始抛出次数whileTrue:max_number=int(str(self.DICE_MAX_DIGIT)*self.throw_time)#抛出最大值nums=[int(str(x),6)forxinrange(0,max_number+1)ifnotset(str(x)).intersection('6789')]#抛出outallpossibledecimalvaluesifmax(nums)+1