在做游戏的时候遇到这个问题:随机指定范围的数字,但不能重复。
一开始的想法是:用一个双循环,直接用【rand()%范围数字】的方法得到一个数字,然后和数据结构里面的已有元素进行比对,如果重复就不添加,退出内层循环,然后一直不断判断直到添加到数据结构里的数量符合要求。代码如下:
void getRandomNum(int range, int count)
{
vector<int> vec;
for (int i=0; i<10; i++)
{
vec.push_back(i);
}
srand(time(NULL));
while (1)
{
int idx = rand() % 60;
vector<int>::iterator itr = vec.begin();
for (; itr != vec.end(); ++itr)
{
if (*itr == idx)
break;
}
if (itr == vec.end())
{
vec.push_back(idx);
if (vec.size() == count)
break;
}
}
}
这种算法效率很低!
这里引入一种效率比这个高很多的办法,简单说一下思路:先用一个数组A保存范围内的每一个数字(当做索引用),然后写个洗牌算法把这个数组的元素随机打乱,最后用另外一个数组B存储数组A的前N个元素的值。这里数组B就是要得到的结果。下面是实现代码:
// 洗牌算法
void shuffle(vector<int>* vec)
{
int size = vec->size();
srand((unsigned)time(nullptr));
for (int i=0; i<size; i++)
{
int temp = (*vec)[i];
int r = rand()%size;
(*vec)[i] = (*vec)[r];
(*vec)[r] = temp;
}
}
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;
}