不知道有多少人是被这个头图骗进来的:)python
事情的原由是这样的,上周有同窗问小编,看着小编的示例代码敲代码,感受本身也会写了,若是不看的话,七七八八可能也写的出来,可是一旦本身独立写一段程序,感受到无从下手。mysql
其实这个很正常,刚开始学习写代码,都是跟着别人的套路往下写,看的套路少,很难造成本身的套路,这就和作数学题是同样的,作一道题就想会全部的题目,这个可能性微乎其微,都是经过大量的练习来摸索到本身的套路。git
正好快过年了,各个公司都会搞一些抽奖活动,小编今天就来聊一下,若是要写一个简单的抽奖程序,小编是怎么写的。github
咱们先整理下思路,目标是什么?sql
目标是要写一个抽奖程序,那么抽奖程序的核心是什么?数据库
固然是如何判断一我的中奖了。那么如何判断一我的中奖呢?缓存
是否是能够经过随机函数来操做呢?并发
一步一步来,咱们先经过随机函数来判断是否中奖。代码是否是能够先写成下面这样:dom
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()复制代码
最终测试结果以下:
小编的人品还能够嘛,直接就能中三等奖。
那么,到这里,是否是一个简易的抽奖程序就算完成了呢?其实还能接着加,若是每一个奖项都有数量限制,而且限制的数量是能够随时调整的等等等等,小编这里就不一一列举了。
总体代码写的稍微有些长了,小编就不贴出来了,上传到代码仓库各位感兴趣的同窗本身访问吧。
注意: 本篇文章所使用代码,仅供演示讲解使用,不可用于生产环境,在访问量过大的状况下会产生严重的性能问题。