如何设等概率的随机函数

来源:互联网 发布:什么软件升级win10 编辑:程序博客网 时间:2024/05/15 04:43
 

如何设等概率的随机函数

分类: 数据结构和算法 程序设计 116人阅读 评论(0) 收藏 举报

目录(?)[+]

本文主要介绍以下主题:

如何设计随机函数,即洗牌算法?

如何设计测试用例检查随机函数的正确性?

随机函数有哪些应用?

如何设计随机函数

Knuth Shuffle的洗牌算法,算法复杂度O(n),洗牌的目的是产生一串等概率的随机列。
1)如何保证等概率:从r个剩余的元素中选择s个元素,那么下一个元素选中的概率为s/r。
2)假设函数bigrand()返回一个大的随机整数(比m和n个大很多),那么bigrand()%n返回[0,n-1]范围的随机整数
4)应用:其他应用如从n个城市中选择出m个城市的名字,可以先对城市名进行签名标记为0,1,...,n-1
记住:我们面对的随机函数的输入是0,1,...,n-1,具体应用可以运用签名转换成成对应的0,1,...,n-1
5)如果要按序输出,可以先对操作对象排序,然后签名[0,n-1],这样通过按序访问数,就能保证输出结果是有序的

[java] view plaincopy
  1. //重新排列[0,n-1]范围内的数  
  2. public int[] knuthShuffle(int[] arr) {    
  3.     if(arr==null)  
  4.       return arr;  
  5.         
  6.     int randValue;    
  7.     for (int remaining=arr.length; remaining>=0; remaining--) {    
  8.         randValue = bigrand()%remaining; //产生[0,remaining-1]范围内的随机数  
  9.         swap(array[remaining], array[randValue]);    
  10.     } //end for   
  11.       
  12.     return arr;  
  13. //end knuthShuffle  
从n个数中随机选择m个数,其中[0,1,...,n-1]代表数的签名

[java] view plaincopy
  1. public void genknuth(int m,int n){  
  2.    //输入控制  
  3.    if(m<=0 || n<=0 || m>n)  
  4.      return ;  
  5.   
  6.    int select=m;     //选择m个元素  
  7.    int remaining=n;  //剩余n个元素  
  8.    int randValue;  
  9.    for(int i=0;i<n;i++){  
  10.       randValue=bigrand()%(remaining-i);   
  11.       if( randValue<select ){  
  12.           System.out.println(i);  
  13.           select--;  
  14.       }//end if  
  15.    }//end for  
  16. }//end genknuth()  

如何设计测试用例检查随机函数的正确性

PC机上是做不出真随机数的,只能做出伪随机数。真随机数需要硬件支持。洗牌洗得好不好,主要是看是不是够随机。概率问题,我们只有一个方法来做测试,那就是用统计的方式。
【测试方法】
      测试洗牌程序也一样,需要通过概率的方式来做统计,是不是每张牌出现在每个位置的次数都是差不多的。
假设对[0,n-1]的n个数产生随机数,调用一次随机函数各个数字产生的概率是1/n,那么如果调用随机概率函数randNumber次,数i在每个位置上出现的次数应该为correctNumber=1/n*randNumber。假设实际数i在某个位置出现的次数是realNumber,则该位置处的误差error=1-realNumber/correctNumber。
      举个例子,如测试样本1000次,列是每个位置出现的次数,行是各个数字的统计,出现概率应该是1/10,也就是理论上出现100次。调用随机函数100次,进行测试效果如下(测试结果来自该博客http://coolshell.cn/articles/8593.html)

       0     1      2       3    4     5      6     7    8    9    
--------------------------------------------------------------
0 |   74  108  123  102   93  198   40   37   52  173
1 |  261 170  114   70    49   28    37   76  116   79
2 |  112 164  168  117   71   37    62   96  116   57
3 |   93   91  119   221  103  66    91   98   78   40
4 |   62   60   82    90    290 112   95   98   71   40
5 |   46   60   63    76    81   318   56   42   70  188
6 |   72   57   68    77    83   39  400  105   55   44
7 |   99   79   70    73    87   34  124  317   78   39
8 |  127  112 102   90    81   24   57   83  248   76
9 |   54   99   91     84    62  144  38   48  116  264

然后统计误差即可。

【测试样本】
设置一个样本大小,做一下统计,然后计算一下误差值是否在可以容忍的范围内。比如:
样本:100万次
最大误差:10%以内
平均误差:5%以内 (或者:90%以上的误差要小于5%)

随机函数有哪些应用

1、创新工场笔试题

第一个是让把一个数组打乱顺序,每个概率相等
2、给定一个函数rand()能产生0到n-1之间的等概率随机数,问如何产生0到m-1之间等概率的随机数?
思路:就是从n个数中随机生成m个数,上面已有代码,此处不做重复说明。
3、如何测试一个随机函数?
4、给定一个函数rand5(),该函数可以随机生成1-5的整数,且生成概率一样。现要求使用该函数构造函数rand7(),使函数rand7()可以随机等概率的生成1-7的整数。
【思路】
我们可以利用rand5() + rand()%3来实现rand7()函数,这个方法确实可以产生1-7之间的随机数,但是是否生成的数字是等概率是?
rand()%3 产生0的概率是1/5,而产生1和2的概率都是2/5,所以这个方法产生6和7的概率大于产生5的概率。如何解决呢?
      rand()产生[0,n-1],把rand()视为n进制的一位数产生器,那么可以使用rand()*n+rand()来产生2位的n进制数,以此类推,可以产生3位,4位,5位...的n进制数。这种按构造n进制数的方式生成的随机数,必定能保证随机,而相反,借助其他方式来使用rand()产生随机数(如 rand5() + rand()%3 )都是不能保证概率平均的。
此题中n为5,因此可以使用rand5()*5+rand5()来产生2位的5进制数,范围就是1到25。再去掉22-25,剩余的除3,以此作为rand7()的产生器。

[java] view plaincopy
  1. public int rand7() {    
  2.     int x = 0;   
  3.     while(x>21)   
  4.     {    
  5.         x = 5 * (rand5()-1) + rand5();  //rand5()生成[1,5]的整数,则rand5()-1生成[0,n-1]的整数  
  6.     }//end while   
  7.     return x%7+1;  //x%7生成[0,7-1]的整数,则x%7+1生成[1,7]的整数  
  8. //end rand7()  
原创粉丝点击