算法设计 --- randX实现randY

简介: 算法设计 --- randX实现randY

算法描述



已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。


不要使用系统的 Math.random() 方法。


进阶:


  1. rand7()调用次数的 期望值 是多少 ?
  2. 你能否尽量少调用 rand7() ?


示例

输入: 3
输出: [8,1,10]
提示:
rand7 已定义。
传入参数: n 表示 rand10 的调用次数。


算法流程



part1:正向公式推导


假设已知rand2()可以均匀的生成[1,2]的随机数,现在想均匀的生成[1,4]的随机数,该如何考虑?

// 很可能首先会考虑相加
rand2() + rand2() = ? ==> [2,4]
   1    +   1     = 2
   1    +   2     = 3
   2    +   1     = 3
   2    +   2     = 4
// 为了把生成随机数的范围规约成[1,n],于是在上一步的结果后减1
(rand2()-1) + rand2() = ? ==> [1,3]
   0       +   1     = 1
   0       +   2     = 2
   1       +   1     = 2
   1       +   2     = 3
// 显然生成的结果不是等概率的,由于值的多种组合导致,必然造成这种结果,尝试如下改动:
(rand2()-1) × 2 + rand2() = ? ==> [1,3]
   0            +   1     = 1
   0            +   2     = 2
   2            +   1     = 3
   2            +   2     = 4


神奇的事情发生了,奇怪的知识增加了。通过这样的处理,得到的结果恰是[1,4]的范围,并且每个数都是等概率取到的。因此,使用这种方法,可以通过rand2()实现rand4()。


也许这么处理只是我运气好,而不具有普适性?那就多来尝试几个例子。比如:

(rand9()-1) × 7 + rand7() = result
     a               b


image.png

总结一下:

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


part2:反向实现


那么想到通过rand4()来实现rand2()呢?这个就很简单了,已知rand4()会均匀产生[1,4]的随机数,通过取余,再加1就可以了。如下所示,结果也是等概率的。

rand4() % 2 + 1 = ?
   1 % 2    + 1 = 2
   2 % 2    + 1 = 1
   3 % 2    + 1 = 2
   4 % 2    + 1 = 1
//事实上,只要rand_N()中N是2的倍数,就都可以用来实现rand2(),反之,若N不是2的倍数,则产生的结果不是等概率的。比如:
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


part3:本题实现(拒绝采样)


有了前面的分析,要实现rand10(),就需要先实现rand_N(),并且保证N大于10且是10的倍数。这样再通过rand_N() % 10 + 1 就可以得到[1,10]范围的随机数了。


而实现rand_N(),我们可以通过part 1中所讲的方法对rand7()进行改造,如下:

(rand7()-1) × 7 + rand7()  ==> rand49()


但是这样实现的N不是10的倍数啊!这该怎么处理?这里就涉及到了“拒绝采样”的知识了,也就是说,如果某个采样结果不在要求的范围内,则丢弃它。基于上面的这些分析,再回头看下面的代码,想必是不难理解了。

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]范围的随机数
        }
    }
}


part4:优化方案


这部分具体的代码是参考官方题解的,不过是我自己在理解了part 1和part 2之后才看懂的,一开始看真不知道为什么(/(ㄒoㄒ)/~~...


根据part 1的分析,我们已经知道(rand7() - 1) * 7 + rand7() 等概率生成[1,49]范围的随机数。而由于我们需要的是10的倍数,因此,不得不舍弃掉[41, 49]这9个数。优化的点就始于——我们能否利用这些范围外的数字,以减少丢弃的值,提高命中率总而提高随机数生成效率。


代码实现


/**
 * The rand7() API is already defined in the parent class SolBase.
 * public int rand7();
 * @return a random integer in the range 1 to 7
 */
class Solution extends SolBase {
    public int rand10() {
        while (true) {
            int a = rand7();
            int b = rand7();
            int num = (a - 1) * 7 + b;  // rand49
            if (num <= 40) return num % 10 + 1;
            a = num - 40; // rand9
            b = rand7();
            num = (a - 1) * 7 + b; // rand63
            if (num <= 60) return num % 10 + 1;
            a = num - 60; // rand3
            b = rand7();
            num = (a - 1) * 7 + b;  //rand21
            if (num <= 20) return num % 10 + 1;
        }
    }
}


总结



本题实现的关键:

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


优化的关键:减少丢弃值的数量,利用拒绝采样,不断更新rand_X()

目录
打赏
0
0
0
0
8
分享
相关文章
macOS Finder显示文件非常慢的解决办法
版权声明:本文为 testcs_dn(微wx笑) 原创文章,非商用自由转载-保持署名-注明出处,谢谢。 https://blog.csdn.net/testcs_dn/article/details/80176086 ...
2351 0
带你读《从基础到应用云上安全航行指南》——一文教你如何从零构建机密计算平台解决方案(1)
带你读《从基础到应用云上安全航行指南》——一文教你如何从零构建机密计算平台解决方案(1)
361 0
高并发架构系列:Redis为什么是单线程、及高并发快的3大原因详解
Redis的高并发和快速原因 1.redis是基于内存的,内存的读写速度非常快;2.redis是单线程的,省去了很多上下文切换线程的时间;3.redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。
4265 0
AI大模型开启智能化新时代
12月19日下午,复旦大学计算机科学技术学院第十二期“步青讲坛”在江湾校区二号交叉学科楼E1006报告厅举行。本期讲坛特别邀请了阿里巴巴集团副总裁、IEEE Fellow叶杰平教授做题为《AI大模型开启智能化新时代》的精彩技术报告。
316 4
智能化AI工具-语言翻译与本地化
在全球化发展的背景下,语言翻译与本地化需求日益增长。无论是跨境电商、国际合作,还是本地化应用开发,都需要高效、准确的翻译解决方案。阿里云通义千问作为一款强大的大语言模型,不仅具备出色的自然语言理解能力,还能够在多语言翻译和本地化场景中发挥重要作用。本博客将详细介绍如何基于阿里云通义千问开发语言翻译与本地化工具,包括产品介绍、程序代码以及阿里云相关产品的具体使用流程。
295 10
|
8月前
一次利用大模型完成Jacoco code coverage报告合并的尝试
本文介绍了利用大模型尝试合并Jacoco代码覆盖率报告的过程。通过定义特定的函数,自动识别并合并两个不同版本项目报告中的相同代码行覆盖率信息。尽管此方法存在局限性,但展示了大模型在自动化编程任务中的潜力。
169 5
面试题:海量数据去重、Top-k、BitMap问题整理
首先直接进入正题,40亿QQ号如何设计算法去重,相同的QQ号码仅保留一个,内存限制为1个G。 (腾讯的QQ号都是4字节正整数,所以QQ号码的个数是43亿左右,理论值2^32-1个,又因为是无符号的,翻倍了一下,所以43亿左右)
面试题:海量数据去重、Top-k、BitMap问题整理
MySQL 优化 index merge(索引合并)引起的死锁分析(强烈推荐)
生产环境出现死锁流水,通过查看死锁日志,看到造成死锁的是两条一样的update语句(只有where条件中的值不同),如下:
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等