【20190826】【查漏补缺】洗牌算法_生成随机数(Knuth Shuffle洗牌算法实现)

问题

设计一个公平的洗牌算法。

什么叫公平?假如有 n 张牌,那么这 n 张牌共有 n!个排列方式(很简单的排列组合思想),所以洗牌中“公平”是指,系统能随机的输出这 n!个结果中的任意一个。暴力解法思想很简单,但时间复杂度很高O(n!),下面学习一下 Knuth Shuffle 洗牌算法,思想也很简单。

Knuth Shuffle 算法从高位到低位进行,依次从前面的元素中随机(用到了random模块)取出一个元素与当前元素互换。

我有一个疑问:为什么是互换?而不是每次直接从剩下的元素中随即取出一个放在高位。针对我的这个疑问,我的想法是互换不需要开辟新的空间,直接在原数组基础上进行操作,降低了空间复杂度。

实习面试的时候有问到类似的问题,但当时我还不懂什么是洗牌算法,所以按自己的想法回答了,现在想想当时的时间复杂度好像回答的不太对。。。(链接:【20190423】【笔经、面经集】2019年暑期实习找工作经历,分享给大家,也给自己长个记性~(持续更新)

(参考:神一样的随机算法

(参考:洗牌算法——python实现

(参考:python 多线程实现洗牌算法 (二)

(参考: 三种洗牌算法shuffle

(参考:浅谈洗牌算法(面试题)


思路及解答

# Knuth Shuffle 洗牌算法
import random
nums = list(range(0, 11))   # 产生 [0, 10] 数组
def swap(a, b):
    a, b = b, a
    return a, b
for i in range(len(nums)-1, -1, -1):   # 从后往前,因为[0, i]的随机数比[i, ...]的随机数更好获得
    tmp_index = random.randint(0, i) % (i + 1)   % 生成 [0,i] 之间的数,就对 (i+1) 取模
    nums[i], nums[tmp_index] = swap(nums[i], nums[tmp_index])
print(nums)

知识点

1. swap 函数(参考: Python为什么不需要swap(a, b)

第一种形式:只是返回了颠倒了的输入值,并没有在数组内部交换两个数组元素。

但如果给这两个返回值名称,那么将改变数组元素。 

第二种形式:直接用 Python 语句实现内部交换。

第三种形式:

下面的错误在于没有给函数返回值,因此没有修改 a, b 的值。 

2. random 模块(参考:Python中的random模块用于生成随机数。

(1) random.random():用于生成 [0, 1.0) 的随机浮点数。

(2) random.randint(a, b):用于生成 [a, b] 的整数。

(3) random.shuffle(List):用于将列表 List 的元素随机打乱输出。

3. O(n!) 时间复杂度

O(n!) 的时间复杂度比指数级的复杂度还高!

4. TypeError: 'range' object does not support item assignment

range() 函数返回的是 "range object",而不是想要的 list 值,因此要用 list(range(n))。

(参考:17个新手常见Python运行时错误

5. 生成 [0, i] 之间的数,就对 (i+1) 取模。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Satisfying

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值