点击上方"蓝字"

关注我们,享更多干货!

程序员节,公司举办了一个抽奖活动,采用的方式是掷六次骰子,组成一个六位数,再对群里的人数取模,计算的结果就是中奖的人的编号。但这种方式公平吗?让我们用Python来验证下。

一、

验证

掷六次骰子,那么这个值就是在111111~666666之间,有6的6次方(即46656)个随机数。

nums = [x for x in range(111111, 666667) if not set(str(x)).intersection('0789')]
print(len(nums))  # 46656

假设群里有134人,用上面46656个数分别对134取模,看最后的结果分布。

total_person = 134
nums_mod = list(map(lambda x: x % total_person, nums))
for i in range(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
....

看数字不直观,我们把它转化为图片:

import matplotlib.pyplot as plt
x = range(0, total_person)
y = [nums_mod.count(i) for i in x]
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(xlabel='person no.', ylabel='prize counts', title='{} person'.format(total_person))
ax.set_xlim(0, total_person)
ax.set_ylim(0, 1000)
plt.show()

可以看到对于群里有134个人,还是很公平的,假设群里又加了一个人,变成135人,那么每人的中奖几率是多少呢?

将total_person改成135,再运行下程序:

编号: 0, 中奖次数: 280
编号: 1, 中奖次数: 577
编号: 2, 中奖次数: 297
编号: 3, 中奖次数: 297
编号: 4, 中奖次数: 297
编号: 5, 中奖次数: 297
编号: 6, 中奖次数: 581
编号: 7, 中奖次数: 284
编号: 8, 中奖次数: 284
编号: 9, 中奖次数: 284
...

这时候就不公平了,中奖次数最少的277,最大的有584,而且中奖次数多的都是对应的编号尾数为1和6。为什么会出现这种现象呢?将前面的代码改造下。

total_person = 135
from collections import defaultdict
for i in range(0, total_person):nums_filter = list(filter(lambda x: x % total_person == i, nums))num_last_number = defaultdict(int)for j in nums_filter:num_last_number[j % 10] += 1print('编号: {}, 中奖次数: {}, 骰子尾数统计: {}'.format(i, len(nums_filter), num_last_number))

可以看到当编号尾数是1或6时,对应的骰子尾数是1或6,而其它的编号对应的骰子尾数都只有一个数字,即0-5,2-2,7-2等。所以才会出现编号尾数为1和6的中奖次数接近其它编号的两倍。

编号: 0, 中奖次数: 280, 骰子尾数统计: defaultdict(<class 'int'>, {5: 280})
编号: 1, 中奖次数: 577, 骰子尾数统计: defaultdict(<class 'int'>, {1: 297, 6: 280})
编号: 2, 中奖次数: 297, 骰子尾数统计: defaultdict(<class 'int'>, {2: 297})
编号: 3, 中奖次数: 297, 骰子尾数统计: defaultdict(<class 'int'>, {3: 297})
编号: 4, 中奖次数: 297, 骰子尾数统计: defaultdict(<class 'int'>, {4: 297})
编号: 5, 中奖次数: 297, 骰子尾数统计: defaultdict(<class 'int'>, {5: 297})
编号: 6, 中奖次数: 581, 骰子尾数统计: defaultdict(<class 'int'>, {1: 284, 6: 297})
编号: 7, 中奖次数: 284, 骰子尾数统计: defaultdict(<class 'int'>, {2: 284})
编号: 8, 中奖次数: 284, 骰子尾数统计: defaultdict(<class 'int'>, {3: 284})
编号: 9, 中奖次数: 284, 骰子尾数统计: defaultdict(<class 'int'>, {4: 284})
...

二、

破局

前面概述提到的办法对于人数是135就不太公平了呀,怎么保证公平呢。公平就是每个人获奖的几率必须是一样。这就要求骰子掷出来的数字要足够随机而且连续。由于单个骰子只有6个不同值,为了让它连续,我们设置骰子的1-6,分别对应数字0-5,而且采用6进制。

比如骰子掷出来的数分别是1、3、4、6、2、5,那么对应的数字就是0、2、3、5、1、4,换算成十进制则为int(‘023514’, 6) = 3430,代码就可以换成如下:

nums = [int(str(x), 6) for x in range(0, 555556) if not set(str(x)).intersection('6789')]
print(len(nums))total_person = 135
nums_mod = list(map(lambda x: x % total_person, nums))
for i in range(0, total_person):print('编号: {}, 中奖次数: {}'.format(i, nums_mod.count(i)))import matplotlib.pyplot as plt
x = range(0, total_person)
y = [nums_mod.count(i) for i in x]
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(xlabel='person no.', ylabel='prize counts', title='{} person'.format(total_person))
ax.set_xlim(0, total_person)
ax.set_ylim(0, 1000)
plt.show()

这才是!

三、

总结

本文由公司的一个小游戏有感而发,主要是想介绍下Python中的map和filter函数,以及matplotlib画图模块。最后附上一个小游戏代码。

from collections import defaultdictclass Prize: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  # 初始投掷次数while True:max_number = int(str(self.DICE_MAX_DIGIT) * self.throw_time)  # 投掷出来的最大值nums = [int(str(x), 6) for x in range(0, max_number+1) if not set(str(x)).intersection('6789')]  # 投掷出来所有可能的十进制值if max(nums) + 1 < self.person_nums:# 如果投掷出来的最大值比总人数少,直接增加投掷次数self.throw_time += 1continueprize_dict = defaultdict(int)for i in nums:prize_dict[i % self.person_nums] += 1percent_diff = (max(prize_dict.values()) - min(prize_dict.values()))/max(prize_dict.values())if percent_diff < self.percent_diff:return self.throw_timeself.throw_time += 1def say(self):self._need_throw_times()print('本次活动人数为{},请依次投掷{}次骰子'.format(self.person_nums, self.throw_time))number_str = ''for i in range(self.throw_time):point = input('第{}次骰子的点数为: '.format(i + 1))if point not in ('1', '2', '3', '4', '5', '6'):raise Exception('点数超出范围')number_str += str(int(point) - 1)int_number_str = int(number_str, 6)print('恭喜{}号中奖!'.format(int_number_str % self.person_nums))if __name__ == '__main__':x = input('请输入活动的人数: ')Prize(int(x)).say()

墨天轮原文链接:https://www.modb.pro/db/151967?sjhy(复制到浏览器或者点击“阅读原文”立即查看)

关于作者

杨豹,国泰君安DBA,爱好Oracle、MySQL,Python。

END

由中国DBA联盟和墨天轮主办的 2021数据技术嘉年华 将于11月19日-20日在北京丽都皇冠假日酒店盛大召开。

大会门票限时0元领取

    扫描下方的二维码即可!

我们在程序员节组织了一场游戏,竟还用Python去验证其公平性?相关推荐

  1. 华彩谢幕:一场游戏一场梦 | 一点财经

    2018年初,很多人认为,瑞典管理学大师斯文·艾里克·肖斯特兰在<管理的两面性>阐述的观点正在腾讯这头大象身上失效. 肖斯特兰认为,成功的领导艺术,能够在理性与非理性元素的交错中找到平衡点 ...

  2. 别把数学想得太难,数学是一场游戏

    数学是一场游戏 我在国内上中学的时候,150分的数学考试,最低考过27分. 27分大家知道是什么概念吗?我们那时候考试用的是答题卡,27分就相当于把这个答题卡放到地上,然后踩两脚,把鞋印放到机器里-- ...

  3. 没钱,就别玩大学这场游戏

    6年前,一个农村的小伙子,以非常优异的成绩(全校第二)考上了一所现在是国家211工程的重点大学,他的志愿里面只填写了一所大学,就是现在这所大学.在亲戚们的帮助下,第一年还可以,虽然父母都是农民,但是第 ...

  4. 《江山美人》:这只是一场游戏

    <江山美人>海报 这只是一场游戏,正如电影中的叛将郭晓冬( blog)的台词"成王败寇"!很多朋友不喜欢这部电影,甚至嘲笑.揶揄在msn之上.<江山美人>不 ...

  5. 一场游戏一场梦------------------大学随手写(11年05月-------11年06月04)

    一场游戏一场梦------------------大学随手写(11年05月-------11年06月04)2011-05-19 13:45 (分类:乱七八糟) 片段一:生活就如同QQ够级 2011年0 ...

  6. 第五人格服务器维护需要多长时间,第五人格:一场游戏多长时间最合理?了解它你就明白为何频频调整...

    Hello!大家好,我是猫寻! 不知道各位小伙伴们在闲暇时间当中是否会思考"一场游戏到底多长时间的设定才最合理"这样的一个问题呢? 其实对于这个问题是很少被玩家们所提及的,毕竟大多 ...

  7. 【经典】一场游戏一场梦、其实你不懂我的心...,N多的英文翻唱歌词

    love in your eyes(驿动的心) I get lost deep in your eyes, and I feel my spirits high, just like the air ...

  8. 服气!3小时竟能写出风靡全球的小游戏,还顺手就赚的盆满钵满

    还记得这款火遍全球的像素小鸟游戏吗? 没有精细的动画,极简的游戏机制.却成为苹果商店下载量最多的免费 app,并为游戏制作人带来了数百万美元的收益. 当姜文说"站着把钱挣了"的时候 ...

  9. Py之tkinter:python最简单的猜字小游戏带你进入python的GUI世界

    Py之tkinter:python最简单的猜字小游戏带你进入python的GUI世界 目录 输出结果 设计思路 输出结果 设计思路 from tkinter import * import tkint ...

最新文章

  1. R语言ggplot2可视化条形图(bar plot)、并为条形图添加误差条(error bar)、自定义设置误差条(error bar)的颜色/色彩( Barplots with Error bar)
  2. 编程珠玑第八章——分治算法求解数组中的最大的连续和
  3. 思科路由和交换限制用户出外网的几种策略
  4. Java StreamTokenizer whitespaceChars()方法及示例
  5. Slog57_玩转NPM之NPM_package的制作发布和使用
  6. 学习HTML的第二次课
  7. 【题集】牛客网·2018年全国多校算法寒假训练营练习比赛(第二场)
  8. 查看apt-get安装软件的版本
  9. 5道经典面试题【转载】
  10. 蓝桥杯 C语言 试题 历届试题 格子刷油漆
  11. 计算机专业的毕设,计算机专业毕设业计要求.doc
  12. 济南oracle 认证费用,济南ORACLE管理培训价格
  13. 地址栏中的#是什么意思
  14. vue回到顶部(常用)
  15. 敏捷 绩效_管理敏捷团队绩效的四个技巧
  16. 小甲鱼Python第十九讲(函数,我的地盘听我的)
  17. 步进电机基础(2.6)-直线步进电机
  18. ping 简单的测试 延时、抖动、丢包率
  19. matlab三大数组之构架数组
  20. ubuntu20.04修改用户名【保留文件】

热门文章

  1. Vmware Linux 固定IP配置(CenOS7)
  2. Servlet相关(servletconfig、servletcontext、request)
  3. 小程序css之圆角边框
  4. Bootstrap 滚动监听插件Scrollspy 的事件
  5. ci mysql 缓存_CodeIgniter框架中启用和清除缓存的教程
  6. vue 修改项目启动后的页面_vue项目打包后打开页面空白解决办法
  7. 十四.激光和惯导LIO-SLAM框架学习之惯导内参标定
  8. mysql 数据库异常_Mysql连接数据库异常汇总【必收藏】
  9. 打印异常堆栈_关于日志打印行号的性能案例
  10. python shutil_Python shutil模块