力扣470 用 Rand7() 实现 Rand10()

Part 1 

已知 rand_N() 可以等概率的生成[1, N]范围的随机数
那么:
(rand_X() - 1) × Y + rand_Y() ==> 可以等概率的生成[1, X * Y]范围的随机数
即实现了 rand_XY()

简单来说就是,前半部分(rand_X-1)*Y是为了保证与后半部分rand_Y的每一位数字依次组合没有重叠。

举例:

rand9()          : {1, 2, 3, 4, 5, 6, 7, 8, 9} 
rand9()-1        : {0, 1, 2, 3, 4, 5, 6, 7, 8}  // rand() 随机数列 + 一个常数,不会改变随机概率
[rand9()-1] * 7  : {0, 7, 14, 21, 28, 35, 42, 49, 56}  // 这里就可以看出端倪了,前半部分数字间的间隔刚好就为7

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
令前半部分[rand9()-1] * 7 为 A,后半部分rand7() 为 B

 A\B | 1 | 2 | 3 | 4 | 5 | 6 | 7
---------------------------------------
 0   | 1 | 2 | 3 | 4 | 5 | 6 | 7 
 7   | 8 | 9 | 10| 11| 12| 13| 14
 14  | .... 懒得打了
 21  | ....
 28  |
 35  |
 42  |
 49  |
 56  |

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

以上参考: 力扣-题解

因此,可以得到以上结论。

Part 2

反之,如果要rand4()实现rand2(),由于需要得到的数在[1,2]区间内,因此需要取余运算,再加1即可。

rand6() % 2 + 1 = ?
   1 % 2    + 1 = 2
   2 % 2    + 1 = 1
   3 % 2    + 1 = 2
   4 % 2    + 1 = 1
   5 % 2    + 1 = 2
   6 % 2    + 1 = 1

rand5() % 2 + 1 = ?
   1 % 2    + 1 = 2
   2 % 2    + 1 = 1
   3 % 2    + 1 = 2
   4 % 2    + 1 = 1
   5 % 2    + 1 = 2

事实上,只要rand_N()中N是2的倍数,就都可以用来实现rand2(),反之,若N不是2的倍数,则产生的结果不是等概率的。

Part 3

要通过rand7()实现rand10(),根据Part1的推论,可以通过(rand7()-1)*7+rand7()=rand49()得到rand49(),但是49并不是10的倍数,于是需要把[41,49]的部分拒绝采样。

class Solution extends SolBase {
    public int rand10() {
        while(true) {
            int num = (rand7() - 1) * 7 + rand7(); // 等概率生成[1,49]范围的随机数
            if(num <= 40) return num % 10 + 1; // 拒绝采样,并返回[1,10]范围的随机数
        }
    }
}

Part 4 - 优化

在Part3中,舍弃了[41,49]的部分,如何才能减少舍弃的数字,提高命中率总而提高随机数生成效率?

class Solution extends SolBase {
    public int rand10() {
        while(true) {
            int a = rand7();
            int b = rand7();
            int num = (a-1)*7 + b; // rand 49
            if(num <= 40) return num % 10 + 1; // 拒绝采样
            
            a = num - 40; // rand 9
            b = rand7();
            num = (a-1)*7 + b; // rand 63
            if(num <= 60) return num % 10 + 1;
            
            a = num - 60; // rand 3
            b = rand7();
            num = (a-1)*7 + b; // rand 21
            if(num <= 20) return num % 10 + 1;
        }
    }
}

参考 力扣-题解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值