利用Python写一个抽奖程序,解密游戏内抽奖的秘密
前言
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。
作者: 极客挖掘机
PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取
http://note.youdao.com/noteshare?id=3054cce4add8a909e784ad934f956cef
分析需求
我们先整理下思路,目标是什么? 目标是要写一个抽奖程序,那么抽奖程序的核心是什么? 当然是如何判断一个人中奖了。那么如何判断一个人中奖呢? 是不是可以通过随机函数来操作呢?
中奖方法
一步一步来,我们先通过随机函数来判断是否中奖。代码是不是可以先写成下面这样:
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()
最终测试结果如下: 小编的人品还可以嘛,直接就能中三等奖。
import random
import pymysql
# 获取中奖配置
def get_lottery_rate():
conn = pymysql.connect(host='114.67.111.196', port = 3306, user='root', password='wsy@123456', 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()
# 判断中奖函数
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
# 判断会员等级中奖率过滤
# 会员等级 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():
# 一等奖中奖次数
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)
# 会员制中奖测试方法
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写一个抽奖程序,解密游戏内抽奖的秘密的更多相关文章
- 学了C语言,如何利用CURL写一个下载程序?—用nmake编译CURL并安装
在这一系列的前一篇文章学了C语言,如何为下载狂人写一个磁盘剩余容量监控程序?中,我们为下载狂人写了一个程序来监视磁盘的剩余容量,防止下载的东西撑爆了硬盘.可是,这两天,他又抱怨他的下载程序不好用,让我 ...
- 利用 Python 写一个颜值测试小工具
我们知道现在有一些利用照片来测试颜值的网站或软件,其实使用 Python 就可以实现这一功能,本文我们使用 Python 来写一个颜值测试小工具. 很多人学习python,不知道从何学起.很多人学习p ...
- 利用python写一个简单的小爬虫 爬虫日记(1)(好好学习)
打开py的IDLE >>>import urllib.request >>>a=urllib.request.urlopen("http://www.ba ...
- 学了C语言,如何利用cURL写一个程序验证某个网址的有效性?
在<C程序设计伴侣>以及这几篇关于cURL的文章中,我们介绍了如何利用cURL写一个下载程序,从网络下载文件.可是当我们在用这个程序下载文件时,又遇到了新问题:如果这个网址是无效的,那么我 ...
- 利用python实现微信小程序游戏跳一跳详细教程
利用python实现微信小程序游戏跳一跳详细教程 1 先安装python 然后再安装pip <a href="http://newmiracle.cn/wp-content/uploa ...
- Python写一个自动点餐程序
Python写一个自动点餐程序 为什么要写这个 公司现在用meican作为点餐渠道,每天规定的时间是早7:00-9:40点餐,有时候我经常容易忘记,或者是在地铁/公交上没办法点餐,所以总是没饭吃,只有 ...
- Python 写一个俄罗斯方块游戏
使用 Python 的 PyGame 库写一个俄罗斯方块游戏的逐步指南 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人 ...
- 【Python】如何基于Python写一个TCP反向连接后门
首发安全客 如何基于Python写一个TCP反向连接后门 https://www.anquanke.com/post/id/92401 0x0 介绍 在Linux系统做未授权测试,我们须准备一个安全的 ...
- 用Python做一个简单的小游戏
学习总是枯燥的,对于Python小白的我来讲,更是乏味的.为了提高学习的兴趣,今天我就来写一个小程序练练手. 数字猜谜游戏相信大家都不陌生,A给出最小值最大值,B写一个该范围内的数,A猜测写下的是多少 ...
随机推荐
- MySQL必知必会(汇总数据, 聚集函数)
SELECT AVG(prod_price) AS avg_price FROM products; #AVG只能用于单个列求平均值,如想计算多个列,必须用多个AVG() SELECT AVG(pro ...
- 自然语言处理(NLP) - 数学基础(1) - 总述
正如我在<2019年总结>里说提到的, 我将开始一系列自然语言处理(NLP)的笔记. 很多人都说, AI并不难啊, 调现有库和云的API就可以啦. 然而实际上并不是这样的. 首先, AI这 ...
- [TimLinux] Python Django myblog启动
1. myblog介绍 自己给自己定的一个学习项目,用于: 使用学到的Python Web开发技术,框架django 使用学到的CSS技术,实现前端功能 使用学到的Axure工具,画出前端页面功能草图 ...
- 源码分析 RocketMQ DLedger(多副本) 之日志复制(传播)
目录 1.DLedgerEntryPusher 1.1 核心类图 1.2 构造方法 1.3 startup 2.EntryDispatcher 详解 2.1 核心类图 2.2 Push 请求类型 2. ...
- ARTS-S pytorch中Conv2d函数padding和stride含义
padding是输入数据最边缘补0的个数,默认是0,即不补0. stride是进行一次卷积后,特征图滑动几格,默认是1,即滑动一格.
- 【CSS】329- 非常强!3行核心css代码的rate评分组件
像rate评分组件一般都用javascript写,所以这次将是一个全新的尝试,用css实现一个rate评分 ❗ 核心代码也就三行? 01 效果图 02 原理 主要是借助radio单选框,梳理如下: 去 ...
- 6种微服务RPC框架,你知道几个?
开源 RPC 框架有哪些呢? 一类是跟某种特定语言平台绑定的,另一类是与语言无关即跨语言平台的. 跟语言平台绑定的开源 RPC 框架主要有下面几种. Dubbo:国内最早开源的 RPC 框架,由阿里巴 ...
- Linux系统基础知识
文件类型属性 '-'代表普通文件 'd'代表目录文件 'l'代表链接文件link 'b'代表块文件block 'c'代表字符设备文件 'p'代表管道文件
- 基于ATxmega128的ASF串口应用
1.编辑串口的配置参数,一般将这些参数放在conf_usart.h配置头文件中,本程序将这些参数放在user_board.h头文件中 #define USART_SERIAL &USARTD0 ...
- C# VS2010 windows服务的安装
可能是太过于懒惰的原因,研究个windows 服务的安装程序都花了大半天时间.在网上看了一些示例,大部分都言过其实,把过程搞得太过复杂,老是需要去研究如何利用InstallUtil.exe及其参数.事 ...