[算法 笔记]用小范围随机函数编写大范围随机函数

  最近笔试出现的一道概率问题:给定一个随机函数rand_m()能按1/m的概率生成数字z≤ z < m),基于rand_m()随机函数编写一个新的随机函数rand_n()1/n的概率生成数字o≤ o < n)。

  在分析前,首先说明有关事件独立的概念:如果两个事件AB满足Pr{A∩B} = Pr{A}Pr{B},则称两个时间独立。例如,假设抛两个均匀硬币的结果是独立的。那么两个都是正面的概率就是(1/2)(1/2=1/4)。

  简化问题:现给定一个随机函数rand3()能按1/3的概率生成数字{0, 1, 2},根据给定的函数写出随机函数rand7(),要求按照1/7的概率生成{0, 1, 2, 3, 4, 5, 6}

  那么,如何来实现这个函数呢?

  (答案来自:http://blog.csdn.net/column/details/ms100.html

 1 int rand_c_opt( int limits )
 2 {
 3     int tmp0 = 0;
 4     int tmp1 = 0;
 5 
 6     do {
 7         tmp0 = rand3() * 3;
 8         tmp1 = rand3();
 9         tmp1 += tmp0;
10     } while( tmp1 > limits );
11 
12     return tmp1;
13 }

  我以前的时候百思不得其解,为什么这么写?而不是这样写:

 1 int rand_c( int limits )
 2 {
 3     int tmp0 = 0;
 4     int tmp1 = 0;
 5     int tmp2 = 0;
 6 
 7     do {
 8         tmp0 = rand3();
 9         tmp1 = rand3();
10         tmp2 = rand3();
11 
12         tmp0 += ( tmp1 + tmp2 );
13     } while( tmp0 > limits );
14 
15     return tmp0;
16 }

  因为tmp0tmp1tmp2的取值范围均在{0, 1, 2}之间,那么三者之和就在[0, 6]范围值内,但是运行结果给我当头一棒。(运行次数为10000000次)

  

  从图中可以看出,这个运行结果不均匀。那么为什么计算结果是这样呢?让我们从概率学的角度来看一下(以前学习的概率知识都让我还给老师了,让我偷偷的在网上找一找): 

  表1 rand3()生成数值的概率

 

数值

0

1

2

概率

1/3

1/3

1/3

 

  那么在tmp1+tmp2的计算结果的概率为:

 

中间计算结果的概率

 

 

01/3

11/3

21/3

01/3

01/9

11/9

21/9

11/3

11/9

21/9

31/9

21/3

21/9

31/9

41/9

 

  则从表2可以计算出04的概率是:

 

中间计算结果概率总结

 

数值

0

1

2

3

4

概率

1/9

2/9

1/3

2/9

1/9

 

   从表3中就发现,在计算的中间过程中,概率就开始不均匀。那么在最后的计算结果tmp0+=(tmp1 + tmp2)的概率是:

 

最后结果的概率

 

 

01/9

12/9

21/3

32/9

41/9

01/3

01/27

12/27

21/9

32/27

41/27

11/3

11/27

22/27

31/9

42/27

51/27

21/3

21/27

32/27

41/9

52/27

61/27

 

   从表4中计算出06的概率是:

 

最后结果的概率总结

 

数值

0

1

2

3

4

5

6

概率

1/27

1/9

2/9

7/27

2/9

1/9

1/27

 

   通过表5,大家可以发现这样的方式计算出的结果是不均匀的(忘记概率知识的结果真是可怕)。  

  函数rand_c_opt()的生成数值过程中,使用3来将第一次随机数值进行了提升,在这样的情况下不改变概率,只是改变生成的结果为{0, 3, 6}。这里有的同学就会问了,那么我不是乘以3而是乘以其他数值也可以。但是,朋友们请注意算式:rand3()*3+rand3()。下面在计算概率的过程中,大家就会发现这个算式的高明之处。至于为什么不能乘以其他数值,在看过之后,大家也可能会想到。

 

算式rand3()*3+rand3()的概率

 

 

01/3

31/3

61/3

01/3

01/9

31/9

61/9

11/3

11/9

41/9

71/9

21/3

21/9

51/9

81/9

 

  通过表6,大家会发现算式rand3()*3+rand3()能够有效以1/9的概率生成08之间的数值。程序的运算结果也能说明:

  

  所有源码:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <time.h>
 4 #include <string.h>
 5 
 6 int rand3()
 7 {
 8     return rand() % 3;
 9 }
10 
11 int rand_c_opt( int limits )
12 {
13     int tmp0 = 0;
14     int tmp1 = 0;
15 
16     do {
17         tmp0 = rand3() * 3;
18         tmp1 = rand3();
19         tmp1 += tmp0;
20     } while( tmp1 > limits );
21 
22     return tmp1;
23 }
24 
25 int rand_c( int limits )
26 {
27     int tmp0 = 0;
28     int tmp1 = 0;
29     int tmp2 = 0;
30 
31     do {
32         tmp0 = rand3();
33         tmp1 = rand3();
34         tmp2 = rand3();
35 
36         tmp0 += ( tmp1 + tmp2 );
37     } while( tmp0 > limits );
38 
39     return tmp0;
40 }
41 
42 int main()
43 {
44     enum{ limits = 9 };
45 //  enum{ limits = 7 };
46     int cnt[limits];
47     int counter = 10000000;
48     srand( (unsigned) time(0) );
49 
50     memset( cnt, 0, sizeof(cnt) );
51 
52     while ( counter-- > 0 )
53     {
54         int tmp = rand_c_opt( limits - 1 );
55     //  int tmp = rand_c( limits - 1 );
56         cnt[tmp]++;
57     }
58 
59     for ( counter = 0; counter < limits; ++counter )
60     {
61         printf( " %d : %d\n", counter, cnt[counter] );
62     }
63 
64     return 0;
65 }
View Code

 

  

  欢迎大家共同来讨论!欢迎指导!!

 

 

 

 

 

posted @ 2013-10-28 15:21  life91  阅读(515)  评论(0编辑  收藏  举报