1. 程式人生 > >洗牌演算法Fisher–Yates shuffle

洗牌演算法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(已排序部分)

隨機數範圍 隨機數 A A(已排序部分)
1–8 6 1 2 3 4 5 8 7 6
隨機數範圍 隨機數 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

--------------------- 本文來自 KangRoger 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/kangroger/article/details/39524825?utm_source=copy