洗牌问题:给定函数RandInt(a, b)(产生最小为a,最大为b的随机数), 将1,2,3,……54这54个整数顺序打乱,并且每个数出现的概率相等。
这是《数据结构与算法分析》中的一道习题,习题中给出了三个算法思路(n=54):
- 按如下方法填入从a[0]到a[n-1]的数组A;为了填入a[i],生成随机数直到它不同于已经生成的a[0],a[1],……,a[i-1],再将其填入a[i];
- 同算法1,但是使用一个状态数组used[]来判断之前是否生成了该数,最初将一个随机数ran放入数组a时,将used[ran]=1,之后产生随机数时先查找used数组确定该随机数是否出现过,若没有,则将其加入数组;
- 初始化数组a[i]=i+1,然后倒序遍历数组a的同时swap(&a[i], &a[RandInt(0,i)]);
第一种方法思路简单但是当N变大时运算量太大,方法2和1的思路相当,但是引入状态数组,降低了复杂度;方法3中,每次生成的是元素下标,只有元素交换,无赋值,所以不会产生重复的元素,可以理解为1/n的概率选一个元素交换到末尾,再以1/(n-1)的概率选剩下元素中的一个交换到倒数第二位,依次下去即可。下面是方法2和3的具体代码:
/*方法2*/
void randsort2(int n){ int a[n], used[n]; int i, t; for(i=0; i<n; i++) used[i]=0; srand(time(NULL)); for (i=0; i<n; i++){ while (1){ t=rand()%n; if (used[t] == 0){ used[t] = 1; a[i] = t+1; break; }else continue; } } // printarray(a, n); }
/*方法3*/
void randsort3(int n){ int a[n]; int i; for (i=0; i<n; i++) a[i] = i+1; for (i=n-1; i>0; i--){ int t = RandInt(0, i); swap(&a[t], &a[i]); } // printarray(a, n); }