我是靠谱客的博主 自信草莓,最近开发中收集的这篇文章主要介绍Python实现乱序算法并模拟验证算法的可行性乱序算法(第一版)尝试改进Shuffle函数(第二版)等概率的洗牌算法(最优解),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

把m个元素在n大小的素组中随机打乱,实验次数为N,来验证随机算法的可行性

文章目录

  • 乱序算法(第一版)
  • 尝试改进Shuffle函数(第二版)
  • 等概率的洗牌算法(最优解)

乱序算法(第一版)

先把所有的需要打乱的样品放在容器的最前面,随机生成一个位置进行交换

# Author: WenRuo 
# CreateDate: 2021/11/29
import random
class KnuthShuffle:
"""
使用一维数组模拟乱序结果是否真随机
"""
def __init__(self, N, n, m):
"""
把m个样品随机分布在n个容器中 统计每一个容器被分配到的有样品的频率是多少
:param N: 试验次数
:param n: 容器总数
:param m: 样品总数
"""
if N <= 0:
print("频次不能小于0")
return
if n < m:
print("样品不能大于等于容器")
return
self.N = N
self.n = n
self.m = m
def run(self):
"""
进行N次概率模拟 先把所有元素排列在数组最前面 然后洗牌打乱
:return:
"""
# 记录频次 初始化都为0 freq[i]的位置指在i的位置出现的频次
freq = []
# 记录容器
arr = []
for i in range(self.n):
freq.append(0)
for i in range(self.n):
arr.append(0)
# 模拟试验次数
for i in range(self.N):
self.reset(arr)
# 先初始化数组 把所有排列到最前面
self.shuffle(arr)
# 洗牌
for j in range(self.n):
freq[j] += arr[j]
# 打印频次
print(">>>索引 : 出现概率")
for i in range(self.n):
msg = "{0} : {1}".format(i, round(freq[i] / self.N, 3))
print(msg)
print(">>> 打印被打乱后的数组:")
for i in arr:
print(i, end=" ")
print("n>>> 打印各个位置出现的频次:")
for i in freq:
print(i, end=" ")
def reset(self, arr):
"""
模拟将样品全部排列在容器的最前面
将arr前m个都设置1,m之后的全部设置成0
:param arr:
:return:
"""
for i in range(self.m):
arr[i] = 1
for i in range(self.m, self.n):
arr[i] = 0
def shuffle(self, arr):
"""
随机打乱
"""
for i in range(self.n):
x = int(random.random() * self.n)
arr[i], arr[x] = arr[x], arr[i]
if __name__ == '__main__':
N = 100000
n = 10
m = 5
exp = KnuthShuffle(N, n, m)
exp.run()

打印结果:

>>>索引 : 出现概率
0 : 0.564
1 : 0.544
2 : 0.519
3 : 0.49
4 : 0.461
5 : 0.47
6 : 0.478
7 : 0.484
8 : 0.492
9 : 0.499
>>> 打印被打乱后的数组:
0 0 0 1 1 1 0 1 1 0
>>> 打印各个位置出现的频次:
56421 54446 51852 49049 46108 46970 47751 48364 49154 49885

0.49与0.56之间的误差过大,说明随机洗牌算法的结果是有偏的,并不能保证每一个位置随机样品的概率是五五开的

尝试改进Shuffle函数(第二版)

由于关注点在m个样品随机打乱,所以每一次把一个样品与随机的另外一个位置进行交换,只需要循环m次即样品的总数即可。

def shuffle(self, arr):
"""
随机打乱
"""
for i in range(self.m):
x = int(random.random() * self.n)
arr[i], arr[x] = arr[x], arr[i]

打印结果

>>>索引 : 出现概率
0 : 0.671
1 : 0.635
2 : 0.593
3 : 0.548
4 : 0.5
5 : 0.413
6 : 0.411
7 : 0.408
8 : 0.407
9 : 0.413
>>> 打印被打乱后的数组:
1 0 1 0 1 0 0 0 1 1
>>> 打印各个位置出现的频次:
67110 63510 59333 54835 50023 41253 41092 40832 40692 41320

0,41-0.67之间的误差更大,比第一版更加有偏。打乱后的数组看似随机,但通过上面的频次结果可以明显观察到随机算法并不合理!!!

上面两种尝试都可以发现随机的结果是有偏的,并不能做到等概率的生成某一种结果之一。对随机的公平性要求比较高的程序,上面的两种洗牌算法都不能接受!

等概率的洗牌算法(最优解)

假设有一副扑克牌,进行洗牌,首先从54张牌中随机一张放在数组的第1个位置,然后从剩下的53张牌中随机选出一张放在数组第二个位置,以此类推。

在不开辟新的空间的情况下,假设有54大小的数组,随机一个数与数组最末尾进行交换,然后从剩下的53个中,再次随机一个数与倒数第二数进行交换,维护一个i–即可

def shuffle(self, arr):
"""
洗牌算法
"""
for i in range(self.n - 1, -1, -1):
x = int(random.random() * (i + 1))
arr[i], arr[x] = arr[x], arr[i]

打印结果:

>>>索引 : 出现概率
0 : 0.501
1 : 0.499
2 : 0.5
3 : 0.501
4 : 0.499
5 : 0.5
6 : 0.497
7 : 0.5
8 : 0.501
9 : 0.503
>>> 打印被打乱后的数组:
1 0 0 0 1 1 1 0 1 0
>>> 打印各个位置出现的频次:
50051 49861 49978 50139 49869 49977 49743 49976 50148 50258

通过结果可以直观的发现频次保持在0.49-0.5 之间,概率差不超过百分之一,说明随机得到的乱序结果保证了足够的乱。是一个较好的算法!

最后

以上就是自信草莓为你收集整理的Python实现乱序算法并模拟验证算法的可行性乱序算法(第一版)尝试改进Shuffle函数(第二版)等概率的洗牌算法(最优解)的全部内容,希望文章能够帮你解决Python实现乱序算法并模拟验证算法的可行性乱序算法(第一版)尝试改进Shuffle函数(第二版)等概率的洗牌算法(最优解)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部