一道迅雷笔试题引发的思考?2012-8-25

铭毅天下
大数据领域优质创作者
博客专家认证
2012-08-25 08:37:31

//求解0-n中(包含0,但不包含n)m个不重复随机数的算法.

void knuth(int n, int m)
{
srand( (unsigned)time( NULL ) );
for( int i = 0; i < n; i++)
{
if( rand()%(n-i) < m)
{
cout << i << "\t";
m--;
}// end if
}//end for
return;
}


不理解点:
1.if( rand()%(n-i) < m)
{
cout << i << "\t";
m--;
}// end if
此处if语句的思路是什么?
个人理解到的:
1)通过m来控制个数。因为题意是求解[0,n-1]的m个不重复的数。
2)rand()%(n-i)即求解的随机数除以(n-i)的余数,此处n-i的值
取为{n,n-1,n-2,...1}.
但串起来还是不太理解此处的思路,望大家指点提示,谢谢!
或者对于求解不重复随机数有没有更好的方法或思路也可以交流。
以前写过,但是比这要复杂很多,这是我见到的最简洁的一种。
...全文
8064 95 打赏 收藏 转发到动态 举报
写回复
用AI写文章
95 条回复
切换为时间正序
请发表友善的回复…
发表回复
lihuifeng1 2012-09-07
  • 打赏
  • 举报
回复
LZ贴出的算法,得到的结果肯定是递增数列,结果不能算是随机,既然要随机,那么结果肯定有不确定性,包括得到的数字和其顺序
Leopard_FF 2012-09-06
  • 打赏
  • 举报
回复

算法不错, 学习了
lihuifeng1 2012-09-06
  • 打赏
  • 举报
回复
把所有的数放到一个数组中,取随机数(随机数在数组长度范围内)当成数组下标取对应数字,取完数字后从数组中剔除该数字,数组长度减一,继续取随机数...

看另外一个题:在一块区域内随机画点,要求在最短时间内把该区域画满。
(答案是把所有坐标值放到一个数组中,每次取随机数,描点,然后从数组中剔除该坐标,继续取随机数...)

这两题是否有异曲同工之妙?
ghao0 2012-09-06
  • 打赏
  • 举报
回复
[Quote=引用 82 楼 的回复:]
楼上的 都没好好看过排列组合 和概率
。。。
[/Quote]
是没学好,并且忘光了。程序似乎是0~n中(包含0,但不包含n)m个不重复随机整数,并且出现的概率似乎为m/n。
kevin_xieh 2012-09-05
  • 打赏
  • 举报
回复
挖,这算法超赞。。。
刚开始也很迷惑不解,为什么他要怎么写,然后自己写了一边,运行了几次,挖。理解了。
感谢楼主分享。
makc_dong 2012-09-04
  • 打赏
  • 举报
回复
这种拼人品的算法有人敢用吗?
其实,可以用标示法,每找到一个数做一个标示,随机长度减一
找数的策略:得一个随机数,在得到这个数去找,如果被标示了,加一,继续找
如果,找到最后一个没找到,从随机数减一上前找,一定可以找到。
哈哈 这个人品不行的也能找的了....
eagle_ice 2012-09-04
  • 打赏
  • 举报
回复
收藏回家研究.
eagle_ice 2012-09-04
  • 打赏
  • 举报
回复
收藏回家研究.
lincc84 2012-09-03
  • 打赏
  • 举报
回复
是不是可以这么理解:N张纸牌,有M张标记牌。N个人依次从中抽取,抽到标记牌的M个人就是我们要的结果。那么:
第一个人抽到的概率是M/N: rand()%(N-0)<M 保证了这一点;
第i+1个人抽到的概率是: 剩下的标记牌数/剩下的牌数M'/(N-i), rand()%(N-i)<M' 同样保证了这一点,实际上因为前面的人抽中后M会自减,所以M'=M, 所以条件可以改写成rand()%(N-i)<M;
综上遍历的时候保证rand()%(N-i)<M即可,(M为剩下的标记牌数)
lincc84 2012-09-03
  • 打赏
  • 举报
回复
是不是可以这么理解:N张纸牌,有M张标记牌。N个人依次从中抽取,抽到标记牌的M个人就是我们要的结果。那么:
第一个人抽到的概率是M/N: rand()%(N-0)<M 保证了这一点;
第i+1个人抽到的概率是: 剩下的标记牌数/剩下的牌数M'/(N-i), rand()%(N-i)<M' 同样保证了这一点,实际上因为前面的人抽中后M会自减,所以M'=M, 所以条件可以改写成rand()%(N-i)<M;
综上遍历的时候保证rand()%(N-i)<M即可,(M为剩下的标记牌数)
lincc84 2012-09-03
  • 打赏
  • 举报
回复
是不是可以这么理解:N张纸牌,有M张标记牌。N个人依次从中抽取,抽到标记牌的M个人就是我们要的结果。那么:
第一个人抽到的概率是M/N: rand()%(N-0)<M 保证了这一点;
第i+1个人抽到的概率是: 剩下的标记牌数/剩下的牌数(M'/N-i), rand()5N-i<M' 同样保证了这一点,实际上因为前面的人抽中后M会自减,所以M'=M, 所以条件可以改写成rand()/N-i<M;
综上遍历的时候保证rand()%(N-i)<M即可,(M为剩下的标记牌数)
luoye 2012-09-02
  • 打赏
  • 举报
回复
挺有意思的,看似简单的几行代码,却不简单
Birchgrove 2012-09-01
  • 打赏
  • 举报
回复
个人觉得此题的本意是:生成一个有m个没有重复数字的有序序列,并且这m个数字在0~n(n>m)之间
ghao0 2012-08-31
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
//求解0-n中(包含0,但不包含n)m个不重复随机数的算法.

C/C++ code


void knuth(int n, int m)
{
srand( (unsigned)time( NULL ) );
for( int i = 0; i < n; i++)
{
if( rand()%(n-i) < m)
{
……
[/Quote]
晕,没看懂:
程序似乎是0~n中(包含0,但不包含n)m个不重复随机整数,并且数字小的出现的概率大。
laocpp 2012-08-31
  • 打赏
  • 举报
回复
关于这个问题,主要有两种思路:
一,产生一个随机数(0-n),检查它是否和已经入选的随机数重复,如果是,重新产生一个随机数,直到这个数不和任何已经入选的随机数重复;重复上述步骤直到产生出m个随机数。
9楼的算法是这种思路的代表。 这种思路的问题是--效率低(m,n都很大时)。 试试 knuth(100, 100)?

那么有没有设么高效一些的方法呢? 这就产生了另一种思路:
二,从 0-n 中随机选取 m 个数,那么这 m 个数必然不重复!
问题所给出的算法恰是采用了这种思路的一个实现,并且保证最多n次循环后,必然可以得到 m 个数:
从 0 开始, 一个个处理 (当前的数是i) :
要不要选中i? 产生一个 0 - (n-i) 的随机数,如果它 < m; 选中i, 并且调低m (下一个数被选中的可能变为C(m-1,n-i)); 否则略过i;
重复处理当前数,直至0-n都处理完(实际上,这里原程序可以作一个小小优化,m == 0 就可退出循环)。
那么这个算法是如何保证 最多n次循环后,必然可以得到 m 个数的呢? 当 i == 0 时, i 被选中的几率是C(m,n); 如果连续k个数没有被选中的话,下一个数被选的几率将变为C(m,n-k);注意: n-k 逼近 m; 如果 n-k == m, 那么后面的每一个数都将选中!
但本人认为这个实现实际上是不正确的, 当 m == n 时, 不论执行多少次,它每次都会给出一个一样的序列 (0 1 2 ... n-1), 那随机性跑哪里去了?

因此有了这种思路的另一种实现,“洗牌”法, 即37楼所提的方法。
这里给出一个简单的类“洗牌”法的实现:

void knuth(int n, int m)
{
int *a = new int[n];
for (int i = 0; i < n; ++i)
a[i] = i;

srand( (unsigned)time( NULL ) );
for( int i = 0; i < n; ++i)
{
int k = rand()%n;
if (k != i)
swap(a[i], a[k]);
}

for (int i = 0; i < m; ++i)
{
cout << a[i] << " ";
}

cout << endl;
delete [] a;
}
天涯倦客 2012-08-31
  • 打赏
  • 举报
回复
楼上的 都没好好看过排列组合 和概率
。。。
天涯倦客 2012-08-30
  • 打赏
  • 举报
回复

调用一次随机就可以的
非要写个循环。。
还折腾了这么多回帖
liuxb0219 2012-08-30
  • 打赏
  • 举报
回复
rand() % (n-i) < m
当n-i=m时,rand % (n-i)产生的余数必然小于m
随着m减小,i相应增大,即从i=n-m增至n过程中n-i=m始终成立。
从而后面m个等式肯定成立,又各个i值不同,所以此算法必然会有0-n中m个不同的数
又rand()%(n-i) < m是个随机事件,所以i值是随机的

证算法的正确性
fox448 2012-08-30
  • 打赏
  • 举报
回复
[Quote=引用 77 楼 的回复:]

我将这段算法用java语言实现了一遍,测试时,发现了一些问题。
Random random = new Random();
int n = 10;
int m = 5;
for (int i = 0; i < n; i++) {
int result = random.nextInt(n);
System.out.println("n : " + n + ", i :……
[/Quote]

输出的随机数为:i 不是随机生成的result。。。
cxflovetl 2012-08-30
  • 打赏
  • 举报
回复
我将这段算法用java语言实现了一遍,测试时,发现了一些问题。
Random random = new Random();
int n = 10;
int m = 5;
for (int i = 0; i < n; i++) {
int result = random.nextInt(n);
System.out.println("n : " + n + ", i : " + i + ", m : " + m + ", if : "
+ (result % (n - i)) + ", result : " + result);
if (result % (n - i) < m) {
System.out.println("result : " + result);
m--;
}
if (m == 0) {
break;
}
}

测试的结果如下:
n : 10, i : 2, m : 4, if : 1, result : 9
result : 9
n : 10, i : 7, m : 1, if : 0, result : 9
result : 9
其中会出现两次 9.
加载更多回复(73)

64,266

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧