洗牌算法Fisher–Yates shuffle

在有个函数int rand(int n)返回1-n之间的随机数。如何把数组A[N]打乱?

最初的Fisher–Yates算法是在另外开辟一个数组B[N],把打算后的A[N]放到B[N]中,步骤如下

设i=0

1、生成1-A.length长度之间的随机数k

2、把A[k]放到B[i],去除数组A中的A[k],i++。

3、如果A.length>0,转到第1步。

这个算法要去除A中的数,每次要移位,所以算法复杂度为O(N^2)。

例如一个算法过程如下:

随机数范围 随机数 A B  
    1 2 3 4 5 6 7 8  
随机数范围 随机数 A B
1–8 3 1 2 3 4 5 6 7 8 3
随机数范围 随机数 A B
1–7 4 1 2 3 4 5 6 7 8 3 5
随机数范围 随机数 A B
1–6 5 1 2 3 4 5 6 7 8 3 5 7
1–5 3 1 2 3 4 5 6 7 8 3 5 7 4
1–4 4 1 2 3 4 5 6 7 8 3 5 7 4 8
1–3 1 1 2 3 4 5 6 7 8 3 5 7 4 8 1
1–2 2 1 2 3 4 5 6 7 8 3 5 7 4 8 1 6
    1 2 3 4 5 6 7 8 3 5 7 4 8 1 6 2

后来算法有了改进,不是另外开辟数组,而是来交换数组A上面的元素来达到重排。

这个算法有2个版本,原理一样:

版本1:

for(int i = n; i>=1; --i)
{
	int j=rand(i);//生成1-i之间的随机数
	exchange(A[i],A[j]);//交换A[i],A[j]
}

版本2:

for(int i = 1; i <= n; ++i)
{
	int j=(rand(n)/n)*(n-i+1)+i-1;//生成i-n之间的随机数
	exchange(A[i],A[j]);//交换A[i],A[j]

版本1的一个演算过程如下:

随机数范围随机数AA(已排序部分)1–861 2 3 4 5 8 76

随机数范围 随机数 A A(已排序部分)
1–7 2 7 3 4 5 8 2 6
随机数范围 随机数 A A(已排序部分)
1–6 6 1 7 3 4 5 8 2 6
1–5 1 5 7 3 4 1 8 2 6
1–4 3 5 7 4 3 1 8 2 6
1–3 3 5 7 4 3 1 8 2 6
1–2 1 7 5 4 3 1 8 2 6

参考:http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

时间: 2024-10-09 23:48:23

洗牌算法Fisher–Yates shuffle的相关文章

Javascript 洗牌算法,打乱数组,随机获取元素

//利用洗牌算法Array.prototype.shuffle=function(){ var i,t,m=this.length; while(m){ i=Math.floor(Math.random()*m--); t=this[m]; this[m]=this[i]; this[i]=t; } return this;} var arr=[1,2,3,4,5];console.log(arr.shuffle());console.log(arr.slice(0,2));

Fisher Yates 洗牌算法

//经典的洗牌算法,数组中随机抽一个元素与最后一个进行交换,下次在前n-1个元素中随机抽,依次类推直到最后一个 void shuffle(CREC *array, long n) { long i, j; CREC tmp; for (i = n - 1; i > 0; i--) { j = rand_long(i + 1); tmp = array[j]; array[j] = array[i]; array[i] = tmp; } } 看到glove/shuffle.c中一个洗牌的小算法,感

随机洗牌算法Knuth Shuffle和错排公式

Knuth随机洗牌算法:譬如现在有54张牌,如何洗牌才能保证随机性.可以这么考虑,从最末尾一张牌开始洗,对于每一张牌,编号在该牌前面的牌中任意一张选一张和当前牌进行交换,直至洗到第一张牌为止.参考代码如下: void knuth() { for (int i = 54; i > 1; i--) { int id = rand() % (i - 1) + 1; swap(a[i], a[id]); } } 由上述方法可知,每一张牌经过洗牌之后一定不会出现在原来位置,那么一共会有多少情况呢,这其实就

算法之洗牌算法

洗牌算法是我们常见的随机问题,在玩游戏.随机排序时经常会碰到,一个最常用的地方就是组卷.它可以抽象成这样:M以内的所有自然数的随机顺序数组. package com.math; import java.util.Random; /** * @author summer * */ public class Shuffle { static final int[] a = new int[54]; static Random rnd = new Random(); static{ for(int i

洗牌算法浅试

Python有自带的洗牌算法函数shuffle(). 自己也通过学习也琢磨了一下它的实现,然后给出一个时间复杂度O(n),空间复杂度O(1)的例子: 1 import random 2 3 def shuffle1(lst) : 4 l = len(lst) 5 if l <= 1 : return lst 6 7 i = 0 8 while l > 1 : 9 j = int(random.random() * l) 10 t = lst[i] 11 lst[i] = lst[i+j] 12

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

很多人都有耳闻过洗牌算法,时常会在面试中碰到,我们下面来定义一下这个问题. 所谓洗牌算法,就是给你一个1到n的序列,让你随机打乱,保证每个数出现在任意一个位置的概率相同,也就是说在n!个的排列中,每一个排列出现的概率相同. 最朴素的做法 对于这个问题我们从最朴素的解法谈起.每次随机选出一个没有被选过的数放到一个队列中,如果随机出来的数已经被选过,那么继续随机直到遇到一个没有被选过的数放入到队列中:重复这样子操作直到所有的数都被选择出来. 我们看看这样子作为什么是对的.首先选第一个数的时候有n个数

【每日算法】洗牌算法

洗牌算法 给定一个n个数的序列,设计一个算法将其随机打乱,保证每个数出现在任意一个位置的概率相同(也就是说在n!个的排列中,每一个排列出现的概率相同). 朴素的做法: 假设输入为数组num[length]. 随机选一个数,放到num[0]中,再随机选数,如果该数已经选过,重新选,直到该数未选过时放入num[1]中,以此类推,直到所有的数都选出来,很明显,这种选法一共有n!中可能,每种可能出现的概率都相同. 但是该做法效率不高,因为选过的数再选将耗费大量时间. 改进的洗牌算法: 基于以上算法的缺陷

洗牌算法Fisher_Yates原理

1.算法 http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 简单的原理如下图所示: 2.原理 总结下,洗牌算法Fisher_Yates的原理就是把从1到n的顺序候选集随机打乱, 做法就是 第1次从1-n的候选集合随机选个数,拿出此数,并把它从候选集合剔除(候选集合n-1). 第2次从1-n-1的候选集合随机选个数,拿出此数,并把它从候选集合剔除(候选集合n-2). 第2次从1-n-2的候选集合随机选个数,拿出此数,并把它从候选集合

洗牌算法与蓄水池抽样

今儿看到了,就在此记录一下吧. 洗牌算法 递归做法:先将1~n-1洗牌,然后取随机数k(0<k<n),并交换n与k,代码很简单: 1 int[] shuffle(int[] cards, int n){ 2 if(n == 1){ 3 return cards; 4 } 5 shuffle(cards, n-1); 6 int k = random(1, n-1); 7 swap(card[k], card[n]); 8 return card; 9 } 也可以转成非递归的: 1 void s