(1)背景
在0~n的范围内,生成m个不同的随机数,每个数出现的概率相同;
或者生成m个0~(m-1)范围内的不同的随机数;
(2)方法一
(2.1)原理
最容易想到的方法,是逐个产生这些随机数,每产生一个,都跟前面的随机
数比较,如果重复,就重新产生。
(2.2)伪代码实现
void get_rand(int *a, int m, int n, int k)//结果存在a中
{
int i,j,t;
for(i = 0; i < k; )
{
t = rand()%(n-m+1)+m;
for(j = 0; j < i; j ++)
if(a[j] == t) break;
if(j == i)//不重复
a[i++] = t;//记录随机数。
}
}
分析,上面中生成一个随机数之后,还需要与之前的数进行对比,效率比较差;可以考虑使用标志位来判断某个数是否被选取过;
如下所示:
/******************************************************************************
*函数名称:voidgenerateDiffRandV2(int a[], int n)
*函数功能:产生互不相同的随机数(产生随机数的范围是1~n-1)
*入口参数:
*返 回 值:无
*
*思 路:先生成一个放置座号的数组,然后从中随机抽取,抽取后为防止重复,立即归零。
* :每次生成座号,只需判断是否为0 即可,大大提高了程序执行的效率。
*******************************************************************************/
void generateDiffRandV2(int a[], int n)
{
int *flag =(int *)malloc(sizeof(int) * n);
static int flag_once = 0;
int i, index;
for(i = 0; i < n; i++)
flag[i]= i+1;
if(!flag_once){
srand(time(0));
flag_once = 1;
}
for(i = 0; i < n;){
index = rand() % n;
if(flag[index] != 0){
a[i++] = flag[index]-1;
flag[index] = 0;
}
}
free(flag);
}
(3)方法二
(3.1)原理
将含有n个元素的数组中元素的位置随机调换;这样只需要一次遍历就可以产生全部的随机数;
(3.2)伪代码实现
下面产生100个100以内不重复随机数的代码:
int a[100];
for(i=0; i<=99; ++i) {
a[i]=i;
}
for(i=99; i>=1; --i) {
swap(a[i],a[rand()%i]);
//因为rand()%i的值的范围为0~(i-1);所以i>=1;
}
说明:此中开始的时候最好以时间为种子的随机数,即srand(time(0));这样保证每次产生的随机数序列都是不同的;
(4)方法三
(4.1)背景
随机产生m个0~n范围内不同的随机数;
(4.2)原理
先用一个数组A保存范围内的每一个数字,然后写个洗牌算法把这个数组的元素随机打乱,最后用另外一个数组B存储数组A的前N个元素的值。这里数组B就是要得到的结果。
(4.3)伪代码实现
// 洗牌算法
void shuffle(vector<int>* vec)
{
int size = vec->size();
srand((unsigned)time(nullptr));
for (int i=1; i<size; i++)
{
int r = rand()%size;
swap(vec, i, r)
}
}
void getRandomCount(int range, int count)
{
vector<int> m_aviable; // 存储结果的数组
vector<int> v_range; // 保存范围的索引值
for (int i=1; i<=range; i++)
{
v_range.push_back(i);
}
// 洗牌
shuffle(&v_range);
// 取洗牌后数组里前count个数。
for (int i=0; i<count; i++)
{
m_aviable.push_back(v_range[i]);
}
// 测试打印
for (int i=0; i<v_range.size(); i++)
{
cout << v_range[i] << "\t";
}
cout << endl;
}
说明:其实此中可以直接一次遍历,进行m次交换即可;