在给定范围中取不重复的随机数

六眼飞鱼酱① 提交于 2020-01-15 02:03:35

在给定范围中取不重复的随机数

    随机取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、百度博客文章,源地址丢失,谢谢原作者

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!