(LeetCode Day1)已经定义了一个可以取1-7的函数,利用此函数写取1-10的函数

(LeetCode Day1)已经定义了一个可以取1-7的函数,利用此函数写取1-10的函数

题目

Given a function rand7 which generates a uniform random integer in the range 1 to 7, write a function rand10 which generates a uniform random integer in the range 1 to 10.

Do NOT use system’s Math.random().

What is the expected value for the number of calls to rand7() function?
Could you minimize the number of calls to rand7()?

输入输出

Example 1:

Input: 1
Output: [7]
Example 2:

Input: 2
Output: [8,4]
Example 3:

Input: 3
Output: [8,1,10]

思路

从1-7到1-10是一个范围不整数性扩增,我们光从1-7的范围很难考虑如何取1-10 的数,但我们从1-49考虑的话,取1-10就比较简单了。
该解法基于一种叫做拒绝采样的方法。主要思想是只要产生一个目标范围内的随机数,则直接返回。如果产生的随机数不在目标范围内,则丢弃该值,重新取样。由于目标范围内的数字被选中的概率相等,这样一个均匀的分布生成了。

显然rand7至少需要执行2次,否则产生不了1-10的数字。通过运行rand7两次,可以生成1-49的整数,
在这里插入图片描述
但是49不是10的倍数,我们把40以上的舍弃

代码

class Solution extends SolBase {
    public int rand10() {
        int a,b,c;
        c = 999;
        while(c>40){
            a = rand7();
            b = rand7();
            c = a+(b-1)*7;//两个数相乘不会得到某些值,所以c是这样取值
        }
        return 1+(c-1)%10;//取余得值
    }
}

由于row范围为1-7,col范围为1-7,这样idx值范围为1-49。大于40的值被丢弃,这样剩下1-40范围内的数字,通过取模返回。下面计算一下得到一个满足1-40范围的数需要进行取样的次数的期望值:

E(# calls to rand7) = 2 * (40/49) +
                      4 * (9/49) * (40/49) +
                      6 * (9/49)2 * (40/49) +
                      ...

                      ∞
                    = ∑ 2k * (9/49)k-1 * (40/49)
                      k=1

                    = (80/49) / (1 - 9/49)2
                    = 2.45

再优化

上面的方法大概需要2.45次调用rand7函数才能得到1个1-10范围的数,下面可以进行再度优化。

对于大于40的数,我们不必马上丢弃,可以对41-49的数减去40可得到1-9的随机数,而rand7可生成1-7的随机数,这样可以生成1-63的随机数。对于1-60我们可以直接返回,而61-63则丢弃,这样需要丢弃的数只有3个,相比前面的9个,效率有所提高。而对于61-63的数,减去60后为1-3,rand7产生1-7,这样可以再度利用产生1-21的数,对于1-20我们则直接返回,对于21则丢弃。这时,丢弃的数就只有1个了,优化又进一步。当然这里面对rand7的调用次数也是增加了的。代码如下:

int rand10Imp() {  
  int a, b, idx;  
  while (true) {  
    a = rand7();  
    b = rand7();  
    idx = b + (a-1)*7;  
    if (idx <= 40)  
      return 1 + (idx-1)%10;  
    a = idx-40;  
    b = rand7();  
    // get uniform dist from 1 - 63  
    idx = b + (a-1)*7;  
    if (idx <= 60)  
      return 1 + (idx-1)%10;  
    a = idx-60;  
    b = rand7();  
    // get uniform dist from 1-21  
    idx = b + (a-1)*7;  
    if (idx <= 20)  
      return 1 + (idx-1)%10;  
  }  
}  

下面计算下优化后方法的调用rand7函数的期望次数:

                 E(# calls to rand7) = 2 * (40/49) +
                  3 * (9/49) * (60/63) +
                  4 * (9/49) * (3/63) * (20/21) +  (9/49) * (3/63) * (1/21) *
                  [ 6 * (40/49) +
                    7 * (9/49) * (60/63) +
                    8 * (9/49) * (3/63) * (20/21) ] +

                  ((9/49) * (3/63) * (1/21))2 *
                  [ 10 * (40/49) +
                    11 * (9/49) * (60/63) +
                    12 * (9/49) * (3/63) * (20/21) ] +
                  ...

                = 2.2123

这里期望次数为2.21,比起未优化的2.45次减少了大概10%。

转载

https://www.cnblogs.com/flybird2014/p/4093176.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值