洗牌算法
这个东西,其实是楼主在为了造一堆恶心的全排列数据,不小心发现的一类很简单的问题QwQ
其实本意只是为了打乱一个全排列数组a的顺序使a[i]<>i 23333【规定原本a[i]=i & i<>0】
可以把它转化成洗牌问题
洗牌,即打乱牌的顺序,越随机越公平。
简单random+判断是否选过:
随机抽一张牌,check一下直到找到没被抽出过的牌,然后把这张牌放入洗好的队列中,重复该过程,直到所有的牌被抽出。
算法复杂度高。。。
Knuth_Shuffle(Fisher–Yates shuffle)
原始版本:
- Write down the numbers from 1 through N.
- Pick a random number k between one and the number of unstruck numbers remaining (inclusive).
- 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.
- Repeat from step 2 until all the numbers have been struck out.
- 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]
参考资料:
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