Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2094829
  • 博文数量: 249
  • 博客积分: 1305
  • 博客等级: 军士长
  • 技术积分: 4733
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-17 10:37
个人简介

不懂的东西还有很多,随着不断的学习,不懂的东西更多,无法消灭更多不懂的东西,那就不断的充实自己吧。 欢迎关注微信公众号:菜鸟的机器学习

文章分类

全部博文(249)

文章存档

2015年(1)

2014年(4)

2013年(208)

2012年(35)

2011年(1)

分类: C/C++

2013-06-15 20:41:00


问题1
    输入:包含两个整数m和n,其中m < n
    输出:0~n-1范围内的m个随机整数的有序列表不允许重复
    从概率的角度说,希望得到没有重复的有序选择,其中每个选择出现的概率相等。
解答:
    该算法依次考虑整数0,1,2,......,n-1,并通过一个适当的随机测试对每个整数进行选择。通过按序访问整数,可以保证输出结果是有序的。
    为了理解选择的标准,考虑m = 2, n = 5的情况。选择第一个整数0的概率是2/5,可以通过下面的语句来实现。
  1.  if(bigrand() % 5) < 2
    但是,我们不能以同样的概率选择来选择整数1:这样做的话,从5个整数里面选出的整数可能是两个也可能是不是两个。
    因此,决策有一些不同:在已经选择0的情况下以1/4的概率选择1,而在未选择0的情况下以2/4的概率选择1。
    一般说来,如果要从r个剩余的整数中选出s个,可以以概率s/r选择下一个数。

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <time.h>
  4. using namespace std;

  5. int bigrand()
  6. {
  7.     srand(unsigned(time(NULL)));
  8.     return RAND_MAX *rand() + rand();
  9. }

  10. int randint(int l, int u)
  11. {
  12.     return l + bigrand() % (u - l + 1);
  13. }

  14. void getknuth(int m, int n)
  15. {
  16.     for(int i = 0; i < n; i ++)
  17.     {
  18.         //select m of remaining n - i
  19.         if(bigrand() % (n - i) < m)
  20.         {
  21.             cout << i << " ";
  22.             m--;
  23.         }
  24.     }
  25.     cout << endl;
  26. }

  27. int main()
  28. {
  29.     int n = 10;
  30.     int m = 5;
  31.     getknuth(m, n);
  32.     return 0;
  33. }
    只要m <= n,选出的整数就恰好为m个:不会选择更多的数,因为m变为0时就不能再选择整数了;也不会选择更少的数,因为当m/n为1时一定会选中一个数。
    for循环语句确保按序输出所有的整数。每个子集被选中的可能性是相等的。

问题二
    上面的算法思想简单,代码很短,所需的空间少。但是算法的运行时间跟n成正比,因此,提出其他解决方案。
    方案一:
    在一个初始化为空的集合里面插入随机整数,直到个数足够。
    这里采用C++标准模板库,用set表示集合。
  1. void gensets(int m, int n)
  2. {    
  3.      set<int> S;
  4.      set<int>::iterator i;
  5.     while (S.size() < m)
  6.     {
  7.         int t = bigrand() % n;
  8.         S.insert(t);
  9.     }
  10.     for (i = S.begin(); i != S.end(); ++i)
  11.     {
  12.         cout << *i << " ";
  13.     }
  14.     cout << endl;
  15. }
    该算法对每个元素的决策是一样的,输出是随机的。
    C++标准模板库规范每次插入都在O(logm)时间内完成,而遍历集合需要O(m)时间,因此此程序的时间复杂度为O(mlogm)(当m相对于n比较小时)。但是,该程序的空间开销比较大。

    方案二:
    生成随机整数的有序子集的另一种方法是把包含整数0~n-1的数组顺序打乱,然后把前m个元素排序输出。打乱n个元素可以采用如下方式。
  1. for(int i = 0; i < n; i++
  2. {
  3.      swap(i, randint(i, n - 1));
  4. }
    这个问题只需要打乱数组的前m个元素即可。
  1. void genshuf(int m, int n)
  2. {    
  3.     int i, j;
  4.     int *x = new int[n];
  5.     for (i = 0; i < n; i++)
  6.     {
  7.         x[i] = i;
  8.     }
  9.     for (i = 0; i < m; i++)
  10.     {
  11.         j = randint(i, n-1);
  12.         int t = x[i];
  13.         x[i] = x[j];
  14.         x[j] = t;
  15.     }
  16.     //排序
  17.     sort(x, x+m);
  18.     for (i = 0; i < m; i++)
  19.     {
  20.         cout << x[i] << " ";
  21.     }
  22.     cout << endl;
  23. }
       该算法需要n个元素的内存空间和O(n+mlogm)的时间。该算法可以看作上面算法的变体:x[0....i-1]表示已经选中元素的集合,x[i....n-1]表示未选中的集合。通过显示的表示未选中的元素,就避免了对新元素是否已经选择的测试。


    



阅读(2540) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~