1. 最朴素暴力的做法.

void cal1()
{
int i = , j = , num = ;
int result[M]; result[] = rand() % N; //第一个肯定不重复, 直接加进去 for (i = ; i < M; i++) //获得剩下的(M-1)个随机数
{
num = rand() % N; //生成0 ~ N之间的随机数字 for (j = ; j < i; j++)
{
if (num == result[j]) //如果和result数组中某个元素重复了
{
i--; //重新开始此次循环
break;
}
} if (j == i) // 说明新产生的数和数组里原有的元素都不同, 则add进去
{
result[i] = num;
}
}
}

2. 在方法1的基础上我们可以进行优化. 每轮遍历n太耗时, 那么优化成logn如何, 于是采用一下set作为辅助, 为了利用它logn的时间复杂度. 代价是多了一个M大小的set的空间.

void cal2()
{
set<int> s;
int num = , index = ;
int result[M]; while (index < M)
{
num = rand() % N;
if (s.find(num) == s.end()) //如果没找到
{
s.insert(num);
result[index++] = num;
}
}
}

3. 如果你被方法1和2无穷的重复比较弄烦了, 可能想到, 每次新产生的随机数都要和已有的数去进行比较是否已存在, 越往后这种方法的效率越低, 不停在做无用功. 那么反其道而行如何? 几斤几两咱们都亮在牌面上, OK, 把所有数先都列出来, 从里面往外筛选, 那么每次选出来的, 肯定是不重复的. 只需要选M次就可以了.

void cal3()
{
int result[M] = {};
deque<int> deq;<span style="white-space:pre"> </span>//队列
int i = , index;   <span style="white-space:pre"> </span>for (i = ; i < N; i++) //初始化, 把所有N个数都放到容器里, 从这里面往外挑, 每次必不重复
{
deq.push_back(i);
} for (i = ; i < M; i++) //挑选出M个数
{
index = rand() % deq.size(); //注意deq.size()是不断变小的, 但是每次都符合随机特性
result[i] = deq.at(index); //把deq数组index位置的元素赋给result[i]
deq.erase(deq.begin() + index); //从deq队列中把该元素删除
}
}

4. 方法3是思路比较理想化和直接, 实际操作中, 你会发现比方法1都要慢很多很多, 原因就出在容器的erase函数, 内部实现的本质是内存片的拷贝, 这个操作相当相当的耗时.这个和语言无关, 换成java等其它语言, 类似的这种函数都是同样的原理. 其实思考到方法3, 真理已经呼之欲出了. 我们沿着这个思路继续优化算法. 从中剔除元素的想法是好的, 但是方法不佳. 其实我们需要的本来就是基本的数组就可以了, 速度还快, 用deque, vector这些容器无非是为了使用他们的erase函数把某个数剔除出去不参与下次的随机过程. 随着一个个数被选出, 容器的大小也在不停变小, 其实使用数组, 利用下标的偏移, 我们直接就可以做到了! 和用数组实现一个队列或者栈不是一样的吗, 无非就是数组下标的移动! 于是沿着方法3的思想, 我们每次随机出来一个下标index(0 <= index < size(size初值为N)), 每次把arr[index]这个位置的元素甩到数组最后面就可以了, 就相当于剔除操作了!

void cal4()
{
int result[M] = {};
int data[N] = {};
int i = , index = ; for (i = ; i < N; i++) //初始化
{
data[i] = i;
} for (i = ; i < M; i++)
{
index = rand() % (N - i);
result[i] = data[index]; //把data数组index位置的元素赋给result[i]
data[index] = data[N - i - ]; //从data数组末尾(这个位置在不停前移)拿一个数替换到该位置, 相当于这个元素被剔除了
}
}

性能测试:

http://blog.csdn.net/aa2650/article/details/12507817

直接输出:

如何用随机数生成0到n之间的m个不重复的数

1、最直接的方法就是先随机生成一个0到n之间的数,判断这个数是否已被选上,如果以前没选过,则选上,如果以前已选,则丢弃

  1. void common(int n,int m)
  2. {
  3. int * randnum=(int *)malloc(n*sizeof(int));
  4. memset(randnum,0,n*sizeof(int));   //把n个位置全部置0
  5. srand(time(NULL));
  6. while(m)
  7. {
  8. int cur=rand()%n;
  9. if (randnum[cur]==0)   //进行判断,如果当前数没有选择过,则选择并输出
  10. {
  11. cout<<cur<<endl;
  12. randnum[cur]=1;
  13. m--;
  14. }
  15. }
  16. free(randnum);
  17. }

这种方法简单易懂,但是需要额外的空间来确保取出的数不重复,那么我们有没有更为简单的方法呢,答案是肯定的

2、先上代码,后做解释

  1. void mRand(int n ,int m)
  2. {
  3. srand(time(NULL));
  4. for (int i=0;i<n;i++)
  5. {
  6. if(rand()%(n-i)<m)
  7. {
  8. cout<<i<<endl;
  9. m--;
  10. }
  11. }
  12. }

上边的代码虽然简洁,但是不易懂,我们接下来说明一下

首先是一个循环,这个循环确保了输出的数是不重复的,因为每次的i都不一样

其次是m个数,在每次循环中都会用rand()%(n-i)<m来判断这个数是否小于m,如果符合条件则m减1,直到为0,说明已经取到m个数了

再次是如何保证这m个数是等概率取到的

在第一次循环中i=0, n-i=n, 则随机数生成的是0-n-1之间的随机数,那么此刻0被取到的概率为 m/n-1
在第二次循环中i=1,n-i=n-1,则随机数生成的是0-n-2之间的随机数,这时1被取到的概率就和上一次循环中0有没有取到有关系了。假设在上一次循环中,没有取,则这次取到的1的概率为 m/n-2;假设上一次循环中,已经取到了,那么这次取到1的概率为m-1/n-2,所以总体上这次被取到的概率为 (1-m/n-1)*(m/n-2)+(m/n-1)*(m-1/n-2),最后通分合并之后的结果为m/n-1和第一次的概率一样的
同理,在第i次循环中,i被取上的概率也为m/n-1

所以这m个数是等概率取到的

参考:http://blog.csdn.net/dlengong/article/details/7932579

转:在0~N(不包括N)范围内随机生成一个长度为M(M <= N)且内容不重复的数组的更多相关文章

  1. C++ 随机生成一个(0,1)之间的小数

    double p; ]; memset(s,,sizeof(s)); s[]='; s[]='.'; ;i<;i++) { s[i]=rand()%+'; } p=atof(s); cout & ...

  2. 随机生成长度为len的密码,且包括大写、小写英文字母和数字

    一道华三面试题,随机生成长度为len的密码,且包括大写.小写英文字母和数字,主要Random类的使用,random.nextInt(len)表示生成[0,len)整数.具体实现见下面代码,已经很详细了 ...

  3. C语言实现随机生成0或1

    rand函数在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生一系列随机数.如果系统提供的种子没有变化,每次调用rand函数生成的伪随机数序列都是一样的.srand(u ...

  4. SQL Server 属性不匹配。存在属性(Directory, Archive),包括属性(0),不包括属性(Archive, Compressed, Encrypted)

    问题:安装SQL SERVER 2008报错 “存在属性(Directory, Archive),包括属性(0),不包括属性(Archive, Compressed, Encrypted)” 解决办法 ...

  5. 微信公众号php从0开发,包括功能(自定义菜单,分享)

    之前写的一篇微信公众号文章. 工作需要,进行此次调研,并记录开发过程. 开发目的,页面授权,页面获取用户头像,用户昵称 微信id, 分享页面. 微信订阅号 无法获取用户个人信息 写在记录前,公众号也是 ...

  6. 用java从0生成一个简单的excel

    用java从0生成一个简单的excel 目标 用代码实现对一个excel的基础操作,包括创建,插入文字,(好像就这些了),生成的excel可以用wps打开,如果直接用c++的文件流会生成假的xls表格 ...

  7. 1 时间戳 2 C# 如何生成一个时间戳 3 js 时间加一分钟... 4 js string->date 5 js 取得当天0点 / 23:59:59 时间

    var str = 'Jan 23, 2019 10:25:47 AM';var strnow = new Date(str); 时间戳(timestamp),一个能表示一份数据在某个特定时间之前已经 ...

  8. 未能加载文件或程序集“Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5”或它的某一个依赖项。系统找不到指定的文件。

    在创建ASP.NET MVC项目过程中发生了这个异常 未能加载文件或程序集"Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0 ...

  9. 未能加载文件或程序集“System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。系统找不到指定的文件

    ASP.NET 运行时错误:针对类型System.Web.Mvc.PreApplicationStartCode的应用程序邓启动初始化方法Start 引发了异常,显示下列错误消息: 未能加载文件或程序 ...

随机推荐

  1. 关于Unity的C#基础学习(二)

    一.Debug的使用 int a=3; Debug.Log("a="+a); 二.整数的定义 int m; Debug.Log(m);  //C#比C更严谨,没有初始化的变量打印出 ...

  2. DTD与XML Schema都是XML文档。(选择1项)

    DTD与XML Schema都是XML文档.(选择1项) A.正确 B.不正确 解答:DTD不是XML文件, schema是XML文档

  3. [ACM] FZU 2086 餐厅点餐 (枚举)

    roblem Description Jack近期喜欢到学校餐厅吃饭.好吃干净还廉价. 在学校餐厅.有a种汤,b种饭.c种面条,d种荤菜,e种素菜. 为了保证膳食搭配,Jack每顿饭都会点1~2样荤菜 ...

  4. c#用run32dll打开系统dll(如系统图片查看器,并置最顶层)

    [DllImport("user32.dll", EntryPoint = "SetWindowPos",CharSet = CharSet.Auto)] st ...

  5. js 文件上传进度条

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. hdu 2857:Mirror and Light(计算几何,点关于直线的对称点,求两线段交点坐标)

    Mirror and Light Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  7. SHGetSpecialFolderPath用法

    The SHGetSpecialFolderPath function retrieves the path of a special folder that is identified by its ...

  8. 响应式布局中为什么要使用em设置字体大小而不用px

    px像素(Pixel).相对长度单位.像素px是相对于不同设备显示器屏幕分辨率(pad/phone/pc)而言的.(引自CSS2.0手册) 1em指的是一个字体的大小,它会继承父级元素的字体大小,因此 ...

  9. Pok&#233;mon Go呼应设计:让全世界玩家疯狂沉迷

    引言:什么样的呼应设计会让移动游戏玩家沉迷?那必须为玩家构建一个属于玩家本人或者被玩家认可的虚拟环境.或者说是被玩家认可的虚拟世界.在移动游戏时代.想要做到这一点并不easy.但Pokémon Go却 ...

  10. Hibernate_day04--QBC查询

    QBC查询 1 使用hql查询需要写hql语句实现,但是使用qbc时候,不需要写语句了,使用方法实现 2 使用qbc时候,操作实体类和属性 3 使用qbc,使用Criteria对象实现 查询所有 1 ...