这个算法是我前几天才听说的,觉得挺有意思,来写一写。好像出处是TAOCP,但我没看过。#(快哭了)

有的时候我们需要打乱一个排列的顺序,比方说在机器学习里面我们通常都会对一个数据集进行shuffle。以前我就用过numpy里面的random.shuffle。但是我当时就没有仔细想过类似这样一个shuffle是如何实现的。我们先看一下Knuth Shuffle的C伪代码,非常简短:

for (int i = n - 1; i >= 0; --i)swap(a[i], a[rand() % (i + 1)]);

其中n是数组的长度,a是待shuffle的数组,swap()交换数组的两项。这里后面对rand()进行一些说明。

首先,我们需要思考一下一个shuffle算法应该满足哪些要求。复杂度不应该太高,OK,Knuth Shuffle满足这个要求。更重要的,通常我们希望这个shuffle之后的序列足够”随机“,这里就出现了公平的概念。什么是公平?我们把这个序列视为1~n的一个排列,那么所有排列就有n!n!n!个。如果shuffle后每个排列出现的可能性相同,即1n!\frac{1}{n!}n!1​,那么就称这个shuffle算法是公平的。实际上这个算法的精髓就在于它的公平性上。

先思考一下,如果我们要获得一个1~n的随机排列,而且生成每种排列都是等可能的,要怎么做?实际上这并不困难。逐一考虑每个位置,对于第一个位置,我们随便等可能地从1~n里挑出一个数,扔到第一个位置。对于第二个位置,我们从剩下的n - 1个数里再等可能地选一个放进去。以此类推,直至放完。
设这样操作得到的排列是p1p2...pnp_1p_2...p_np1​p2​...pn​,即第一次挑出了数字p1p_1p1​,第i次挑出数字pip_ipi​。再记shuffle后第i个位置的值是XiX_iXi​。那么根据过程便有P(X1=p1)=1n,P(X2=p2∣X1=p1)=1n−1,⋯,P(Xi=pi∣X1=p1,X2=p2,...,Xi−1=pi−1)=1n−(i−1)P(X_1 = p_1) = \frac{1}{n}, P(X_2 = p_2 | X_1 = p_1) = \frac{1}{n - 1}, \cdots, P(X_i = p_i | X_1 = p_1, X_2 = p_2, ..., X_{i-1} = p_{i-1}) = \frac{1}{n-(i-1)}P(X1​=p1​)=n1​,P(X2​=p2​∣X1​=p1​)=n−11​,⋯,P(Xi​=pi​∣X1​=p1​,X2​=p2​,...,Xi−1​=pi−1​)=n−(i−1)1​,把它们乘起来就得到P(X1=p1,X2=p2,...,Xn=pn)=1n∗1n−1∗⋯12∗1=1n!P(X_1 = p_1, X_2 = p_2, ..., X_n = p_n) = \frac{1}{n} * \frac{1}{n-1} * \cdots \frac{1}{2} * 1 = \frac{1}{n!}P(X1​=p1​,X2​=p2​,...,Xn​=pn​)=n1​∗n−11​∗⋯21​∗1=n!1​。好的。

再来看Knuth Shuffle算法,它其实就是上述过程,只不过是为了代码书写方便从最后一个位置开始向前挑。这里对于rand()的要求便是它产生的随机数应该是均匀的,或者说rand() % (i + 1)对应着能等可能地产生0 ~ i这i + 1个整数的均匀分布。a[rand() % (i + 1)]即从剩下的i + 1个数里面随机挑选了一个出来。我们不希望任何一个数被重复挑选,这个数已经被挑过了,怎么办?swap一举两得,它不仅把挑出来的数放置在当前正在考虑的位置i上,而且使得数组当前的[0,i)[0, i)[0,i)项是剩下的还没被挑选的数,[i,n)[i, n)[i,n)项是已经安排好位置的数,因为i是递减的,每个数被安排好位置之后便不再会受到影响。

明白了这个过程,也可以正着来写。这回我们让数组项对应的下标是[1,n][1, n][1,n],数组前面一部分是已经安排好的,后面一部分是等待被挑选的。

for (int i = 1; i <= n; ++i)swap(a[i], a[i + rand() % (n - i + 1)]); // select an index from [i, n]

关于Knuth Shuffle算法相关推荐

  1. Fisher–Yates shuffle 算法

    简单来说 Fisher–Yates shuffle 算法是一个用来将一个有限集合生成一个随机排列的算法(数组随机排序).这个算法生成的随机排列是等概率的.同时这个算法非常高效. Fisher–Yate ...

  2. Knuth Shuffle

    Knuth Shuffle 问题描述 算法描述 算法实现 算法证明 算法分析 延伸问题 不改变原有数组 算法描述 算法实现 算法证明 未知长度的原始数组 算法实现 产生一个随机的环排列 算法描述 算法 ...

  3. 随机排列实现 -shuffle算法

    随机排列实现 -shuffle算法 洗牌算法(shuffle):原理是,一个数A,随机选一个在这个数A之前的下标,将这个下标对应数字与A对换:之后不再考虑最后的这一个数: 类似于无放回的抽签问题: f ...

  4. NCR Shuffle算法原理

    NCR Shuffle算法原理 刘永胜    2006年于广州 NCR的Shuffle(混淆)算法,做ATM的很多都知道,并且都使用有封装好的现成接口,但是基本没有多少人知道其算法原理,所以将原理写在 ...

  5. C#:实现随机洗牌Knuth-Durstenfeld Shuffle算法​(附完整源码)

    C#:实现随机洗牌Knuth-Durstenfeld Shuffle算法 public static void Shuffle<T>(T[] a) {Random rnd = new Ra ...

  6. shuffle算法c语言,C#Shuffle算法(洗牌算法、抽样算法)

    Fisher-Yates Shuffle算法 1.创建一个新的list 2.随机取出当前0-list.Count其中一个数 3.把老list当前随机数位置添加到新list 4.老list删除这个数 5 ...

  7. JS中数组随机排序实现(原地算法sort/shuffle算法)

  8. Knuth 洗牌算法

    核心思想 洗牌算法(Knuth shuffle算法):对于有n个元素的数组来说,为了保证洗牌的公平性,应该要能够等概率的洗出n!种结果. 举例解释如下: 开始数组中有五个元素: 在前五个数中随机选一个 ...

  9. 大厂面试为什么总考算法?以及如何避开算法面试。

    点击蓝色"五分钟学算法"关注我哟 加个"星标",天天中午 12:15,一起学算法 作者 | liuyubobobo 来源 | 是不是很酷 大厂面试为什么总是考算 ...

最新文章

  1. 创建bdlink密码是数字_如何创建实际上是安全的密码
  2. Delphi XE5 常见问题解答
  3. html中投影效果图,利用CSS3(box shadow)制作边框投影
  4. php 单元测试 麻烦,php – 正确的单元测试
  5. python学习:语句
  6. 16. 3Sum Closest 最接近的三数之和
  7. 跳出所有循环的语句_从零开始的Java之旅2.0 流程控制语句
  8. YOLOv3改进方法增加特征尺度和训练层数
  9. 油猴脚本(tampermonkey)推荐与下载
  10. 螺旋模型的概念简答题
  11. 什么叫DMZ区?DMZ区它有什么作用?
  12. Android App集成支付宝
  13. VS2019源码,通过基址+偏移修改软件内阳光数值(植物大战僵尸单机版)
  14. 一句话题解(20170801~20170125)
  15. 这是你不曾见过的最全的ASO应用市场优化方式(中篇)
  16. 三星note10 android q,当之无愧的安卓机皇!三星Note10+测评,结果难以想象
  17. 基于改进鲸鱼优化算法的WSN覆盖优化
  18. 斑马射频打印机同时操作打印、写入EPC、读TID
  19. 转自博客园- 林梦然+++opencv下指定文件夹下的图片灰度化(图片的读取与保存)-------简单记录
  20. outlook插入html文件,Outlook正文插入附件?简单设置即可!

热门文章

  1. vue采用 XLSX文件上传与下载
  2. 转 Android视频播放软解与硬解的区别
  3. 《计算机图形学原理及实践》学习笔记之第十一章
  4. 分享!如何分分钟实现微信扫二维码调用外部浏览器打开指定页面的功能
  5. kvm虚拟化软件和Linux 系统 的安装
  6. cad菜单栏快捷键_天正CAD界面“菜单栏”不见了怎么办?教你3招秒解决,实用神技巧...
  7. 知乎python储存_模拟知乎登录——Python3
  8. 提升从改变开始...
  9. 校园一键报警柱的作用
  10. vm虚拟机安装mac os系统