在给定范围中取不重复的随机数
随机取m个数(在1到n的范围之内),(m<=n),要求m个数没有重复。有没有什么好的算法,时间复杂度和空间复杂度都很好?
方法一:用STL中的set集,红黑树来处理
取随机数可以用C++标准的rand,至于M个不重复,用std::set来解决,把取到的随机数插入到set里面,通过set的size()==m来判断是否已取够m个了。
#include <set>
#include <stdlib.h>
int main()
{
std::set<int> s;
while(1)
{
int r = rand()%n;
s.insert(r);
if(s.size() == m)
{
break;
}//if
}//while
}
由于set底层实现是红黑树,插入复杂度是对数级。
方法二:
#include <iostream>
#include <cstdlib> //用于rand()和srand()函数
#include <ctime> //设置不同的随机数
using namespace std;
int main (){
srand( time( 0 ) ); //调用不重复的随机数函数
unsigned i;
for ( int n = 0; n++ < 10; )
{
i = rand() ; //对i 赋系统的随机数
cout << " The NO." << n << "is : " << i << endl;
}
return 0;
}
1、 C++标准函数库提供一随机数生成器rand,返回0~RAND_MAX之间均匀分布的伪随机整数。 RAND_MAX必须至少为32767。rand()函数不接受参数,默认以1为种子(即起始值)。随机数生成器总是以相同的种子开始,所以形成的伪随机数列也相同。失去了随机意义。
2、C++中另一函数srand(),可以指定不同的数(无符号整数变元)为种子。但是如果种子相同,伪随机数列也相同。
3、比较理想的是用变化的数,比如时间来作为随机数生成器的种子。
在头文件ctime中时间库包含time函数,它可以返回一个表示时间、日期、月和年的数值使用如下调用可将该值设为rand的种子
srand(static_cast<unsigned>(time(static_cast<time_t*>(NULL))));
4. 但srand()并不是说使随机数都不一样,它只是使取随机数的种子随着时间而改变。
方法三:以下方法需要附加空间b数组,我们还是假设100个数据。
(1)将范围数组b[100](b[i]=100+i,不妨设数组下标从1开始)的每个元素设置一个标志位flag,初始均为flag=0;若某元素被选入到a数组中,则flag=1;显然,以后再选到重复元素可以立刻判定是否已选。但是仍然有一个很严重的问题,在小规模输入下,无疑它的表现是不错的。但现在举一个失败的例子.
在1~65536之间,选择65500个不重复的随机数,看看后面的随机数,比如第65500个数(最后一个),它要在剩下的36个数中选择才会有flag=0(根本不知道这36个数是什么)。
改进:先在1~65536之间随机选取36个数,删除.将剩下的65500个数依次赋值给a[65500],然后打乱顺序即可。
当范围数组与目标数组的大小非常接近时,上述算法非常有效,建议采用。
(2)问题的最终解决.
仍以最开始的那个例子来说,初始数组b[i]=100+i,a数组空。(a是我们需要的存放生成的随即数的数组,我们需要从b数组中取数)
每次随机生成数组b的一个下标subscript,然后取出它所对应的数据b[subscript],记下来给a当前的存储单位a[j]。然后将数组b的最后一个数b[length]覆盖b[subscript]的位置,同时将数组a指向下一个数,b数组的长度减1。尽管前若干次生成的下标subscript随机数有可能相同,但因为每一次都把最后一个数填到取出的位置,因此,相同下标subscript对应的数却绝不会相同,每一次取出的数都不会一样,这样,就保证了算法的确定性、有效性、有穷性。
参考:
1、百度博客文章,源地址丢失,谢谢原作者
来源:CSDN
作者:hopegrace
链接:https://blog.csdn.net/hopegrace/article/details/103976800