洗牌算法

 

这个东西,其实是楼主在为了造一堆恶心的全排列数据,不小心发现的一类很简单的问题QwQ

其实本意只是为了打乱一个全排列数组a的顺序使a[i]<>i   23333【规定原本a[i]=i  &   i<>0】 

可以把它转化成洗牌问题

洗牌,即打乱牌的顺序,越随机越公平。

简单random+判断是否选过:

随机抽一张牌,check一下直到找到没被抽出过的牌,然后把这张牌放入洗好的队列中,重复该过程,直到所有的牌被抽出。

    算法复杂度高。。。

Knuth_Shuffle(Fisher–Yates shuffle)

  原始版本:

  1. Write down the numbers from 1 through N.
  2. Pick a random number k between one and the number of unstruck numbers remaining (inclusive).
  3. Counting from the low end, strike out the kth number not yet struck out, and write it down at the end of a separate list.
  4. Repeat from step 2 until all the numbers have been struck out.
  5. The sequence of numbers written down in step 3 is now a random permutation of the original numbers.                       from wikipedia

今天的版本:

    从后往前,与前面剩下的i-1个中随机一个交换【注意:pascal中random(k)得出的结果为0~k-1】

例如生成长度为10的随机全排列  ,

 1 program Knuth_Shuffle;
 2 const n=10;
 3 var
 4   i:longint;
 5   a:array[0..1000] of longint;
 6 procedure swap(x,y:longint);
 7 var
 8   tmp:longint;
 9 begin
10   tmp:=a[x];
11   a[x]:=a[y];
12   a[y]:=tmp;
13 end;
14 begin
15   for i:= 0 to n-1 do a[i]:=i+1;
16   randomize;
17   for i:= n-1 downto 0 do
18     swap(i,random(i));
19   for i:= 0 to n-1 do write(a[i],' ');
20 end.

 时间复杂度O(n),正如wiki上说的和O(n^2)相比是“naïve implementation” 

当然你也可以反过来写  ——伪代:

1 -- To shuffle an array a of n elements (indices 0..n-1):
2 for i from 0 to n−2 do
3      j ← random integer such that i ≤ j < n
4      exchange a[i] and a[j]

 

 

一些洗牌算法的演示:戳这    或者    here

 

参考资料:

https://en.wikipedia.org/wiki/Fisher–Yates_shuffle  Fisher–Yates shuffle in wikipedia

http://coolshell.cn/articles/8593.html 《如何测试洗牌程序》 陈皓

http://blog.csdn.net/dwyane_mys/article/details/8053896 洗牌算法之Knuth Shuffle

 

posted @ 2017-02-09 19:55  bobble  阅读(299)  评论(0编辑  收藏  举报