在有个函数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 |
随机数范围 | 随机数 | A | B |
---|---|---|---|
1–7 | 4 | 1 2 |
3 5 |
随机数范围 | 随机数 | A | B |
---|---|---|---|
1–6 | 5 | 1 2 |
3 5 7 |
1–5 | 3 | 1 2 |
3 5 7 4 |
1–4 | 4 | 1 2 |
3 5 7 4 8 |
1–3 | 1 | 3 5 7 4 8 1 | |
1–2 | 2 | 3 5 7 4 8 1 6 | |
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 | 1 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