如何用 Python 写一个简易的抽奖程序

不知道有多少人是被这个头图骗进来的:)
事情的起因是这样的,上周有同学问小编,看着小编的示例代码敲代码,感觉自己也会写了,如果不看的话,七七八八可能也写的出来,但是一旦自己独立写一段程序,感觉到无从下手。
其实这个很正常,刚开始学习写代码,都是跟着别人的套路往下写,看的套路少,很难形成自己的套路,这就和做数学题是一样的,做一道题就想会所有的题目,这个可能性微乎其微,都是通过大量的练习来摸索到自己的套路。
正好快过年了,各个公司都会搞一些抽奖活动,小编今天就来聊一下,如果要写一个简单的抽奖程序,小编是怎么写的。
分析需求
我们先整理下思路,目标是什么?
目标是要写一个抽奖程序,那么抽奖程序的核心是什么?
当然是如何判断一个人中奖了。那么如何判断一个人中奖呢?
是不是可以通过随机函数来操作呢?
中奖方法
一步一步来,我们先通过随机函数来判断是否中奖。代码是不是可以先写成下面这样:
import random
# 判断中奖函数
def lottery():
flag = random.randint(0, 9)
if flag < 2:
return True
else:
return False
首先,我们获取 0 ~ 9 之间的随机正整数(这里不讨论 random 是不是真随机,从狭义上来讲我们可以认为它是随机的),如果中奖率为 20% 的话,我们可以认为小于 2 的数字为中奖,其余的为没有中奖。然后中奖后返回 True ,没有中奖返回 False 。
我们加一个入口测试函数,测试一下上面的代码是否能正常运行,并且中奖率是否能维持在大约 20 % 左右。
if __name__ == '__main__':
# 中奖次数
a = 0
# 没有中奖次数
b = 0
for i in range(1000000) :
if (lottery()):
a += 1
else:
b += 1
print('共计中奖:', a, ',未中奖:', b)
执行结果:
共计中奖: 200145 ,未中奖: 799855
上面的测试总共循环了 1 百万次,大约执行需要 2 ~ 3 秒左右,速度还是蛮快的。可以看到,中奖结果确实接近 20% 左右。
动态中奖率
难道到这里就结束了么?当然不可能,这里只是刚刚开了个头。
如果这时老板说,你这个概率不能调整啊,需要让中奖率可以动态调整的,活动刚开始的时候中奖率要高,随着时间的推移,中奖率要降下来。
这时候咋整,傻眼了吧。
既然中奖率要可调整,那么我们中奖率就不能定死在程序中了,这个中奖率需要有一个地方去做存储,在每次做随机的时候将这个中奖率取出来。
简单易行的方法就是将这个中奖率放在数据库中或者缓存服务中,这个根据实际业务场景来定。一般是根据预估访问压力的大小来进行技术选型,如果压力不是特别大,那么放在数据库中也是可以的,如果并发会比较高的话,建议还是放在缓存中。
我们来写一个从数据库获取中奖概率的方法(为了展示直观,小编这里直接使用 Mysql 数据库用作数据存储),先看下数据库的数据:

很简单的设计了一张表,里面有意义的字段有两个,一个用作中奖率的分子部分,一个用作中奖率的分母部分。分母部分最好要设置成 100 、 1000 、 10000 这种,这样计算中奖率会比较好计算。
def get_lottery_rate():
conn = pymysql.connect(host='localhost', user='root', password='password', database='test', charset='utf8mb4')
try:
sql = 'SELECT fenzi, fenmu FROM rate'
cursor = conn.cursor()
cursor.execute(sql)
result = cursor.fetchone()
return result
except Exception as ex:
print(ex)
finally:
conn.close()
运行这个方法测试结果如下:
(10, 100)
可以看到,我们获得了一个元组,里面的内容就是我们从数据库取出来的分子和分母。
我们将前面的抽奖的那个方法改一下,改成从数据库获取中奖比例。修改后的代码如下:
def lottery():
rate = get_lottery_rate()
flag = random.randint(1, rate[1])
if flag < rate[0]:
return True
else:
return False
还是运行上面的测试方法,这里要注意下,因为我们现在是从数据库获取数据,每次方法执行都要加上数据库链接的建立与销毁,建议将循环次数修改为 1000 以内,不然执行的时间就有点太长了。
小编这里将循环次数修改为 1000 次后,执行结果如下:
共计中奖: 92 ,未中奖: 908
那么到这里,我们就可以通过修改数据库中数据实时的操作中奖率了。当然上面的慢的问题我们可以使用数据库连接池等技术进行优化。
增加奖项
那么是否就结束了呢?no no no,我们接着加需求。
现在,我们只能知道每次到底中不中奖,只有一个奖项,但是现在想变成 3 个奖项,如:一等奖、二等奖、三等奖那该怎么办?
这个对之前的抽奖方法改动就有点大了,首先我们先在数据库增加出来另外两个奖项的配置:

配置这里三个奖项的分母最好保持一致,否则后续计算会徒增复杂度。
修改我们获取配置的那个方法:
def get_lottery_rate():
conn = pymysql.connect(host='localhost', port = 3306, user='root', password='password', database='test', charset='utf8mb4')
try:
sql = 'SELECT * FROM rate order by id asc '
cursor = conn.cursor()
cursor.execute(sql)
result = cursor.fetchall()
return result
except Exception as ex:
print(ex)
finally:
conn.close()
测试调用后结果如下:
((1, 10, 100), (2, 5, 100), (3, 1, 100))
先在我们要做的是要将这个配置融入进我们之前的中奖的那个方法中,不多说,直接上代码:
# 判断中奖函数
def lottery():
config = get_lottery_rate()
flag = random.randint(1, config[0][2])
if flag <= config[0][1]:
return 1
elif flag > config[0][1] and flag <= (config[1][1] + config[0][1]):
return 2
elif flag > (config[1][1] + config[0][1]) and flag <= (config[2][1] + config[1][1]):
return 3
else:
return 0
接着修改我们的做测试的代码:
def main():
# 一等奖中奖次数
a = 0
# 二等奖中奖次数
b = 0
# 三等奖中奖次数
c = 0
# 未中奖次数
d = 0
# 循环次数
e = 0
for i in range(1000):
e += 1
print('当前循环次数:', e)
result = lottery()
print('当前中奖结果:', result)
if (result == 1):
a += 1
elif (result == 2):
b += 1
elif (result == 3):
c += 1
else:
d += 1
print('一等奖中奖:', a, ',二等奖中奖次数:', b, ',三等奖中奖次数:', c, ',未中奖次数:', d)
调用我们的测试方法:
if __name__ == '__main__':
main()
小编这里的运行结果如下:

增加会员判断
到这里我们还没完,还能加需求,现在网站大多数都是会员制的,比如白银会员,黄金会员,钻石会员,如果不同的会员等级需要有不同的中奖率,这个是很正常的一件事儿,小编现在还清晰的记得当年某家大型互联网公司代码中的注释 “穷逼 VIP(活动送的那种)” 。
我们假设钻石会员的中奖率为整体中奖率的 100% ,黄金会员的中奖率为整体中奖率的 50% ,白银会员的中奖率为整体中奖率的 20% 。
最简单的实现方式是直接在最外层套一层会员中奖率的判断,不知道各位同学怎么想。
小编这里给出自己的解决方案:
# 判断会员等级中奖率过滤
# 会员等级 1.白银会员 2.黄金会员 3. 钻石会员
def vip_lottery(level):
rate = random.randint(1, 10)
# 如果是钻石会员,直接进入抽奖函数
if level == 3:
return lottery()
# 如果是黄金会员, 50% 概率进入抽奖函数
elif level == 2:
if rate <= 5:
return lottery()
else:
return 0
# 如果是白银会员, 20% 概率进入抽奖函数
elif level == 1:
if rate <= 2:
return lottery()
else:
return 0
# 如果是其他,直接返回未中奖
else:
return 0
我们新增一个测试增加会员过滤的测试方法:
# 会员制中奖测试方法
def test_vip():
print('请输入您当前的会员等级:1.白银会员 2.黄金会员 3. 钻石会员')
level = input()
result = vip_lottery(int(level))
if (result == 1):
print('恭喜您中了一等奖')
elif (result == 2):
print('恭喜您中了二等奖')
elif (result == 3):
print('恭喜您中了三等奖')
else:
print('未中奖,谢谢惠顾')
在我们的入口函数中调用这个方法:
if __name__ == '__main__':
test_vip()
最终测试结果如下:

小编的人品还可以嘛,直接就能中三等奖。
那么,到这里,是不是一个简易的抽奖程序就算完成了呢?其实还能接着加,如果每个奖项都有数量限制,并且限制的数量是可以随时调整的等等等等,小编这里就不一一列举了。
整体代码写的稍微有些长了,小编就不贴出来了,上传到代码仓库各位感兴趣的同学自己访问吧。
注意: 本篇文章所使用代码,仅供演示讲解使用,不可用于生产环境,在访问量过大的情况下会产生严重的性能问题。
示例代码
如何用 Python 写一个简易的抽奖程序的更多相关文章
- Python写一个自动点餐程序
Python写一个自动点餐程序 为什么要写这个 公司现在用meican作为点餐渠道,每天规定的时间是早7:00-9:40点餐,有时候我经常容易忘记,或者是在地铁/公交上没办法点餐,所以总是没饭吃,只有 ...
- 自动化测试(二)如何用python写一个用户登陆功能
需求信息: 写一个判断登录的程序: 输入: username password 最大错误次数是3次,输入3次都没有登录成功,提示错误次数达到上限 需要判断输入是否为空,什么也不输入,输入一个空格.n个 ...
- 自动化测试(三)如何用python写一个函数,这个函数的功能是,传入一个数字,产生N条邮箱,产生的邮箱不能重复。
写一个函数,这个函数的功能是,传入一个数字,产生N条邮箱,产生的邮箱不能重复.邮箱前面的长度是6-12之间,产生的邮箱必须包含大写字母.小写字母.数字和特殊字符 和上一期一样 代码中间有段比较混沌 有 ...
- 如何用Python写一个计算器软件 附带效果图
该计算器使用Python tkinter模块开发 效果如下图 import tkinter #导入tkinter模块 root = tkinter.Tk() root.minsize(280,500 ...
- 如何用Python写一个贪吃蛇AI
前言 这两天在网上看到一张让人涨姿势的图片,图片中展示的是贪吃蛇游戏, 估计大部分人都玩过.但如果仅仅是贪吃蛇游戏,那么它就没有什么让人涨姿势的地方了. 问题的关键在于,图片中的贪吃蛇真的很贪吃XD, ...
- 如何用Python写一个每分每时每天的定时程序
1.计算生日是星期几 当你女朋友要过生日了,你肯定要定找家饭店订个餐庆祝一下,餐馆工作日会空一些,周末位置不好定,要是能知道她的生日是星期几就好了,下面这个程序就能搞定~~ 比如girl friend ...
- Python练习册 第 0013 题: 用 Python 写一个爬图片的程序,爬 这个链接里的日本妹子图片 :-),(http://tieba.baidu.com/p/2166231880)
这道题是一道爬虫练习题,需要爬链接http://tieba.baidu.com/p/2166231880里的所有妹子图片,点进链接看一下,这位妹子是日本著名性感女演员--杉本由美,^_^好漂亮啊,赶紧 ...
- 十行代码--用python写一个USB病毒 (知乎 DeepWeaver)
昨天在上厕所的时候突发奇想,当你把usb插进去的时候,能不能自动执行usb上的程序.查了一下,发现只有windows上可以,具体的大家也可以搜索(搜索关键词usb autorun)到.但是,如果我想, ...
- 【Python】如何基于Python写一个TCP反向连接后门
首发安全客 如何基于Python写一个TCP反向连接后门 https://www.anquanke.com/post/id/92401 0x0 介绍 在Linux系统做未授权测试,我们须准备一个安全的 ...
随机推荐
- NOIP模拟 24
连续爆炸的开端. 从这一场开始我没状态了 T1 star way to heaven 受强降雨boboQQQ影响,我一直认为这是一道和凸包有关的计算几何题 很快就弃了,除了期望没做过带实数的题,所以吓 ...
- 使用Typescript重构axios(二十六)——添加HTTP授权auth属性
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- vue中组件的data为什么是一个函数
1. 前言 在学习vue的时候,一直纳闷一件事:组件的data数据为什么必须要以函数返回的形式,为什么不是简单的对象形式呢?遂带着问题去翻官方文档,文档中自然也写明了这么做的原因,本篇博文以官方文档给 ...
- thinkphp5.1长连接-单例模式测试!
在控制器中 使用以下代码测试 for ($i = 0; $i < 1000; $i++) { $tmp['name'] = 'f_'.$i; $tmp['times'] = date('Y-m ...
- css3 transform做动画
css3 transform做动画第一种用关键帧 这里就不说了 就说第二种方法用 transition属性 ps:1jquery anim不支持transform动画 但css还是支. 2 css3关 ...
- PHP判断数组下标有没有存在的方法
PHP判断数组下标有没有存在的方法<pre>if(!empty($token['errcode'])){ print_r($token['errmsg']); exit();}</p ...
- node.js评论列表和添加购物车数据库表创建
2.1:评论列表--发表评论 用户点击新闻列表某一条新闻,看到新闻详细发表评论 -用户输入评论内容 -发表评论 [将用户评论内容保存数据库 xz_comment] 2.2:评论列表--发表评论-开发评 ...
- 致和我一样迷茫的Java程序员们
缘起 从事近7年Java开发之后,在2019年这个寒冷的冬天里,我终于迎来了人生中的第一次裁员. 啊,30岁之后的裁员真让人焦虑. 按照以往惯例,在面试心仪的公司之前,需要先面试一些不那么心仪的公司热 ...
- Asciinema:你的所有操作都将被录制
如何实现类似于Jumpserver koko一样的终端录制回放功能呢?本文介绍一个神器 asciinema 是一款开源免费的终端录制工具,它可以将命令行输入输出的任何内容加上时间保存在文件中,同时还提 ...
- 解决vue低版本安卓手机兼容性问题
低版本的安卓手机可能会白屏,是由新特性不支持引起的 解决代码es6新特性兼容问题 1,npm 安装 npm install babel-polyfill npm install es6-promise ...