这次我们要生成前N个正整数的一个随机排列
书中给出 三种算法 如下:
前提:需要一个随机数生成函数randInt(begin,end),生成从begin 到 end 的随机数
int randInt(int begin, int end)
{
int temp;
temp = begin + (rand() % (end - begin + 1));
return temp;
}
算法一:
依次填入a[0]到a[n-1]的数组a,每次都不断调用随机数函数,直到生成的随机不同于已经生成的a[0],a[1],...a[i-1],然后填入a[i];
——时间复杂度为O(N²logN),实现省略,但是时间复杂度分析如下:
每一次成功的概率为(N − i)/N,故每一次需要实验的次数为N/(N − i),然后还要检测与前i个数的不同O(i),后
算法二:
同算法一,但是要保存一个附加的数组,称之为used数组,用来记录随机数是有已经被使用,而不用从前到后一次比较:
(貌似又是一个用空间换取时间的例子)
——时间复杂度为O(NlogN),实现如下:
//O(NlogN)
int randInt(int begin, int end)
{
int temp;
temp = begin + (rand() % (end - begin + 1));
return temp;
}
void randomPermutation1(int n)
{
int *a = new int[n];
bool *used = new bool[n];
for (int i = 0; i < n; i++)
used[i] = true;
int k = 0;
for (int i = 0; i < n; ++i) {
k = randInt(1, n);
//这里是O(logN)
while (used[k] == false) {
k = randInt(1, n);
}
used[k] = false;
a[i] = k;
}
cout << "{ ";
for (int i = 0; i < n; i++)
cout << a[i] << ",";
cout << " }";
}
算法三:
依次填写数组a[i]=i+1;然后再次遍历都是让这些数随机交换
——时间复杂度为O(N),实现如下:
void randomPermutation2(int n)
{
int *a = new int[n];
for (int i = 0; i < n; ++i)
a[i] = i + 1;
//从0开始毫无意义,每次的交换相当于让这个位置取范围内的随机值
for (int j = 1; j < n; ++j)
swap(a[j], a[randInt(0, j)]);
cout << "{ ";
for (int i = 0; i < n; i++)
cout << a[i] << ",";
cout << " }";
}
最后加上主函数main(),不要忽视设置随机数种子:
int main()
{
//不要每次都设置随机数种子,例如在for里面,那么结果可能相同
int n;
cin >> n;
time_t ts;
srand((unsigned int)time(&ts));
randomPermutation1(n);
randomPermutation2(n);
system("pause");
}