用 Python3 做一个可’作弊’随机数生成器,并对结果进行图表分析
前一段时间,因为班上都比较懒的原因,很多活动,都因为同学抵不住游戏的诱惑,而出现没有人参加的尴尬场面。而每个班又有必须要去人。这时候,就出现需求,要给班上的同学进行按照学号的抽取。于是,就应运而生了,**第一代的随机数生成器**。
import random
import os
Start = int(input("请输入最小值:")) #
End = int(input("请输入最大值:"))
Num = int(input("请输入生成数值的个数:"))
count = 1
print("随机生成数值:\n")
while count <= Num:
RandNum = random.randint(Start, End)
# 用random.randint()模块,生成一个Start到End的一个随机数
print(" 第",count, "个随机数值:", RandNum)
count = count + 1
print("\n")
os.system("pause")
这个就是第一代的随机数生成器,但后来发现,这个有个很 **重大的缺陷**
就是,数字是可以不断重复的,比如我一个室友在抽取一周晚自习卫生的安排中,5次中枪3次。
在一顿暴打后,我决定要改进。
最开始,我就打算用一个 ==dict字典== 去存储这些信息,==key== 为数字,==value== 为该数字出现的次数。 每一次生成一个随机数,都去==dict字典==中,查询是该数字是否超过了重复的上限。
于是,==第二代的随机数生成器==,出现了
while count <= Num: # 检测是否超过了所需要的最大数 数量的个数
RandNum = random.randint(minLim, maxLim) # 生成一个随机整数(数的极限为minList和maxList)
if RandNum in reNumberDict.keys(): # 如果随机数在重复字典的键中存在
if reNumberDict[RandNum] == reLimit: # 如果该键出现次数等于reLimit(重复最大次数),则放弃该值
continue
else:
reNumberDict[RandNum] = reNumberDict[RandNum] + 1 # 如果以上条件都满足,则给该键的值加1,表示出现次数加1
else: # 如果该随机数没有在重复字典的键中出现过,则添加该键,并给其值为1,表示该键出现一次
reNumberDict[RandNum] = 1
randomNumberlist.append(RandNum) # 将一个随机数加入到randomNumberList(最后随机数结果的存储list)
count = count + 1 # count计数器加1
然后,这个版本,解决了,重复的问题。
在一个夜深人静的晚上,我室友突然爬到我的床边,对我说
“你的这个小程序,可以做个小功能嘛?就是可以让一些数字不出现。这样,就抽不到我了。”
伴随着室友, 满脸奸笑的脸,躺在床上的我,突然有了一个大胆的想法。
想偷懒不搞卫生的念头愈来愈大,于是,我飞速爬起床。去完成这个功能。
我的想法是,用类去封装一系列的功能:
- 用一个方法,将我要作弊的号码储存在一个json文件中
- 用一个方法,读取json文件中的号码,并且储存在一个变量list中
- 用一个方法,生成随机数,并且每一次生成一个随机数,就去这个数字判断是否存在于那个要作弊的变量中,如果存在,就跳过,不做处理,跳过本次循环。
然后,在一段时间后,就出现了第三代随机数生成器
class randomNumber:
"""
在设定的范围内,随机生成设定个数的随机数,并且可以设置每个数的重复次数
设有backDoor机制
"""
def __init__(self, minLim, maxLim, Num, reLimit=0, backDoor=False, backDoorJsonPath=""):
self.minLim = minLim # 随机数的起始值(包含)
self.maxLim = maxLim # 随机数的最大值(包含)
self.Num = Num # 生成随机数的个数
self.reLimit = reLimit # 每个数的生成次数上限,默认值为 0,表示不限次数
self.backDoor = backDoor # backdoor开关
self.backDoorList = [] # backDoor列表
self.randomNumberlist = [] # 生成的随机数的list
self.reNumberDict = {} # 随机数的重复次数dict
self.randomStatus = True # random()运行标识符
self.backDoorJsonPath = backDoorJsonPath # backDoorJson的path
if backDoor == True: # 如果backdoor是打开的
self.backDoor = backDoor # 则设置类内backDoor开关为打开
if os.path.exists(backDoorJsonPath): # 检查是否存在backDoorJsonPath文件
self.backDoorJsonPath = backDoorJsonPath # 如果存在则设置类全局backDoorJsonPath
else: # 如果不存在,则设置backDoor开关为关闭
self.backDoor = False
else:
pass
if (((maxLim - minLim) - self.backDoorJsonReader()) + 1) * reLimit < Num:
self.randomStatus = False # 改变random()运行运行标识符为False
print("Init_Error: 初始化错误,请保证 最大可能数字出现总次数 大于 您所需要随机数个数!!!!")
def backDoorJsonReader(self):
"""
读取backDoorJson的数据,并赋值给self.backDoorList
:return:返回self.backDoorList的长度
"""
if self.backDoor: # 如果backDoor开关为打开的,则将json文件内容(list)读取到self.backDoorList
jsonManager = JsonM(self.backDoorJsonPath)
self.backDoorList = jsonManager.readerContent
return len(self.backDoorList) # 返回self.backDoorList的长度
else:
return 0
def backDoorJsonWriter(self, inBackDoorList):
"""
写入backDoorJson的数据
:param inBackDoorList: 需要写入的backDoor数据
:return:无返回值
"""
if os.path.exists(self.backDoorJsonPath): # 如果存在backDoorJsonPath文件
jsonManager = JsonM(self.backDoorJsonPath) # 实例化JsonM,初始化里会读取backDoorJsonPath文件的json
jsonManager.jsonContent = jsonManager.readerContent + inBackDoorList
# 将json文件的list和inBackDoorList拼接,并赋值给jsonContent
jsonManager.jsonContent = list(set(jsonManager.jsonContent)) # 清除列表中的重复元素
jsonManager.writer() # 将jsonContent写入文件
else:
jsonManager = JsonM(self.backDoorJsonPath, inBackDoorList)
def randomCount(self):
"""
生成随机数的次数统计,并写入到reNumberDict.json
:return:无返回值
"""
if os.path.exists("reNumberDict.json"): # 如果存在backDoorJsonPath文件
jsonManager = JsonM("reNumberDict.json") # 实例化JsonM,初始化里会读取backDoorJsonPath文件的json
jsonManager.jsonContent = self.reNumberDict
# 将json文件的list和inBackDoorList拼接,并赋值给jsonContent
jsonManager.writer() # 将jsonContent写入文件
else:
jsonManager = JsonM("reNumberDict.json", self.reNumberDict)
def random(self):
"""
内置random版(相对性能低一点)用于按条件生成随机数,并把符合条件的随机数赋值给self.randomNumberlist
:return:无返回值
"""
self.backDoorJsonReader()
if self.randomStatus: # 检测是否达成运行条件
pass
else: # 不达到就强制退出
return
count = 1 # 设置计数器,这个是为了计算生成了几个数
while count <= self.Num: # 检测是否超过了所需要的最大数 数量的个数
RandNum = random.randint(self.minLim, self.maxLim) # 生成一个随机整数(数的极限为minList和maxList)
if RandNum in self.backDoorList: # 如果该随机数出现在backDoorList之中,则跳出本次循环
continue
else:
pass
if RandNum in self.reNumberDict.keys(): # 如果随机数在重复字典的键中存在
if self.reNumberDict[RandNum] == self.reLimit: # 如果该键出现次数等于reLimit(重复最大次数),则放弃该值
continue
else:
self.reNumberDict[RandNum] = self.reNumberDict[RandNum] + 1 # 如果以上条件都满足,则给该键的值加1,表示出现次数加1
else: # 如果该随机数没有在重复字典的键中出现过,则添加该键,并给其值为1,表示该键出现一次
self.reNumberDict[RandNum] = 1
self.randomNumberlist.append(RandNum) # 将一个随机数加入到randomNumberList
count = count + 1 # count计数器加1
def numpyRandom(self):
"""
高性能版Random,使用了numpy库。
用于按条件生成随机数,并把符合条件的随机数赋值给self.randomNumberlist
:return:无返回值
"""
self.backDoorJsonReader()
if self.randomStatus: # 检测是否达成循环运行条件
print("成功")
if numpyStatus: # 检测是否成功引入了numpy
pass
else:
print("未成功引用Numpy模块,已使用内置备用随机数生成器") # 弹出提示
self.random() # 运行内置random的随机数生成器
else: # 不达到就强制退出
return
count = 1 # 设置计数器,这个是为了计算生成了几个数
while count <= self.Num: # 检测是否超过了所需要的最大数 数量的个数
RandNum = numpy.random.randint(self.minLim, self.maxLim + 1, size=1) # 生成一个随机整数(数的极限为minList和maxList)
if RandNum in self.backDoorList: # 如果该随机数出现在backDoorList之中,则跳出本次循环
continue
else:
pass
if RandNum.tolist()[0] in self.reNumberDict.keys(): # 如果随机数在重复字典的键中存在
if self.reNumberDict[RandNum.tolist()[0]] == self.reLimit: # 如果该键出现次数等于reLimit(重复最大次数),则放弃该值
continue
else:
self.reNumberDict[RandNum.tolist()[0]] = self.reNumberDict[RandNum] + 1 # 如果以上条件都满足,则给该键的值加1,表示出现次数加1
else: # 如果该随机数没有在重复字典的键中出现过,则添加该键,并给其值为1,表示该键出现一次
self.reNumberDict[RandNum.tolist()[0]] = 1
self.randomNumberlist.append(RandNum.tolist()[0]) # 将一个随机数加入到randomNumberList
count = count + 1 # count计数器加1
在这个代码中,我还用numpy的random.randint()模块写了个高性能版的random函数,具体性能高了
多少我也不知道,但是据说,应该在生成数很多的时候,会有体现。
然后,之后,又在班上用了这个版本,我和室友成功,再也没有被抽取去搞卫生了。
有一天,隔壁班长要用它抽取一下班上的一个值日安排。一次就随机了300次,据他说是多按了一下0
不过,在我嘲笑他眼瞎手残的时候,他突然问了我个问题。
如果我们要抽取的数据次数很多,并且可重复次数也多,怎么去统计呢?
我告诉他,我的类里,有一个方法储存了一个json,里面有数据的重复次数。然后我们打开,瞬间就懵了
因为,数据太多了,我们这2个四眼仔没法承受这样密密麻麻数据的冲击。
于是,我想到一个库 Matplotlib
我可以用它配合那个json的内容,生成一个柱状图。这样数据不可视化了吗?
于是,就在类中又加入了一个方法。
def reNumberDictViewer(self):
"""
用numpy和matplotlib.pyplot对reNumberDict(数字的重复次数统计),并绘制二维柱状图
:return: 无返回值
"""
if numpyStatus: # 检测是否成功引入了numpy
if pltStatus: # 检测是否成功引入了matplotlib.pyplot
if len(self.randomNumberlist) == 0 or len(self.randomNumberlist) != self.Num:
# 如果self.randomNumberlist 为空,或者长度不为需求所需,则不运行
return
else:
pass
else:
print("未成功引入matplotlib绘图模块,未完成绘图功能")
return
else:
print("为成功引入Numpy模块,未完成绘图功能")
return
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文
x = numpy.array(list(self.reNumberDict.keys())) # 把self.reNumberDict的 键 依次存入有序列表 x 中
y = numpy.array(list(self.reNumberDict.values())) # 把self.reNumberDict的 值 依次存入有序列表 y 中
count = 1 # 定义一个循环控制器
xy = [] # 定义一个装有 数据柱 标注 的坐标的list
while (count < len(x)): # 用循环将x和y的值,一一对应的放入temp,然后装到xy中
temp = []
temp.append(x[count - 1])
temp.append(y[count - 1])
xy.append(temp)
count = count + 1 # 计数器自加一
title = str(self.minLim) + " 号到 " + str(self.maxLim) + " 号的共 " + str(self.Num) + " 次随机抽取情况"
# 动态赋值title的内容
plt.figure(title, figsize=(self.maxLim / 2, 4)) # 设置figure的宽度和高度,以及figure的标题
plt.title(title) # 设置图表的标题
plt.bar(x, y, 0.5, alpha=1, color='b') # 设置柱状图的x,y信息,柱子的宽度,透明度,颜色
plt.xticks(numpy.arange(self.minLim, self.maxLim + 1, 1)) # 设置x轴显示的信息
plt.yticks(numpy.arange(1, self.reLimit + 1, 1)) # 设置y轴显示的信息
for i in xy:
plt.annotate("%s" % i[1], xy=i, xytext=(i[0] - 0.1, i[1] + 0.025)) # 用循环,依次标记柱状图的柱子的值
plt.show()
这样,就用 Matplotlib 实现了数据的可视化。
这是我写的第一篇博客,有问题还希望各位大神指教,谢谢!