zoukankan      html  css  js  c++  java
  • [算法 笔记]用小范围随机函数编写大范围随机函数

      最近笔试出现的一道概率问题:给定一个随机函数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
    ", counter, cnt[counter] );
    62     }
    63 
    64     return 0;
    65 }
    View Code

     

      

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

     

     

  • 相关阅读:
    数据库01 /Mysql初识、基本指令、数据库密码相关、创建用户及授权
    数据库/MySQL的安装
    Python并发编程06 /阻塞、异步调用/同步调用、异步回调函数、线程queue、事件event、协程
    Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池
    Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁
    Python并发编程03 /僵孤进程,孤儿进程、进程互斥锁,进程队列、进程之间的通信
    python并发编程02 /多进程、进程的创建、进程PID、join方法、进程对象属性、守护进程
    Python并发编程01 /操作系统发展史、多进程理论
    java继承内部类问题(java编程思想笔记)
    递归遍历文件夹中的文件
  • 原文地址:https://www.cnblogs.com/life91/p/3392513.html
Copyright © 2011-2022 走看看