经典面试问题:用 randX() 实现 randY()

在牛牛空间上看面经的时候经常会看到这样一个问题,就是现在有 randX()可以等概率给出 1\sim X范围内的随机数,现在要根据这个来实现 randY()

1. 当 X > Y

其实很容易想到,当用大随机数生成小随机数时可以直接舍弃掉超出范围内的结果,只保留 1\sim Y 范围内的结果,显然生成结果是等概率的,为了使算法更快,我们可以只舍弃掉最后的 X \bmod Y 个数

int randY(int Y) {
    int res;
    do {
        res = randX();
    } while(res > X - X % Y);
    return (res % Y) + 1;
}

期望次数为({m:= X - X \bmod Y})

\begin{align*}\lim_{n\to \infty}S_n = \lim_{n\to\infty}(\frac{m}{X}+(\frac{m}{X})^2+\cdots +(\frac{m}{X})^n) = \frac{m}{X-m}\end{align*}

2. 当 X < Y

现在忘掉原问题,改为: 现在有一个 randX()randY() 如何等概率地生成 1\sim X\times Y 内的数呢?其实很简单,可以把它想象为一个有 XY 列的矩阵,如何将这个二维矩阵映射到一维的 1\sim X\times Y 范围内,对于位置 (i, j) (从1开始) ,只需要构造映射函数为 (i - 1)\times Y + j 即可。自然地: 可以使用 (randX() - 1) * Y + randY() 来等概率地生成 1\sim X\times Y 内的数了。

接下来,考虑 3 维矩阵映射到 1 维,即现在有 randX()randY()randZ() 要映射到 1\sim X\times Y\times Z ,可以通过 (randX - 1) * (Y * Z) + (randY - 1) * Z + randZ() 来实现

得出一般性的结论,一个 n 维矩阵,矩阵维度分别为 d_1\times d_2\times \cdots d_n ,而 (i_1,i_2,\cdots,i_n) 表示元素的 n 维坐标,则一维映射公式可以表示为(令 d_{n+1} = 1 )

\begin{align*}\mathrm{index} = \sum_{k = 1}^n (i_k - 1)\times (\prod_{j = k+1}^{n+1} d_j) + 1\end{align*}

映射函数只需要将公式里的 i_k 分别替换为对应的 Rand() 函数即可。

现在回到问题,怎么通过 randX() 来实现 randY() 呢?

很简单,我们只需要进行多次采样,假设每次采样的结果为 1\sim d_i(d_i\le X) ,然后通过 d_i 的组合

\begin{align*}d_1\times d_2\times \cdots d_n \ge Y\end{align*}

通过之前构造的映射函数,我们实现了 1\sim d_1\times d_2\times \cdots d_nrand() 方法,获取 randY() 就只需要像情况1一样把多余的部分舍弃掉就好了。至于怎么通过 randX() 来获取 randd_i 呢?还是和情况1一样。

理论上来说,我们可以随意的按照想要的方式对采样方式进行组合,只要满足 d_1\times d_2\times \cdots d_n \ge Y 就行了,但是不同的组合方式在性能上却不同,我们总是想要找出一种最优的组合方式。

其实现在的问题就转化成为了,需要找一个最大质因子不超过 X 的数 m(m\ge Y) ,然后对 m 进行质因数分解,如此就能知道需要进行多少组采样,而在具体操作中,可以将一些较小的 d_i 进行合并,以减少采样次数,比如 X = 7d_1 = 2d_2 = 3 ,就不需要进行两次采样,而是进行 d' = d_1\times d_2 = 6 一次采样。

例: 用 rand7() 构造 rand100()

100 分解质因数后为 2\times 2\times 5\times 5 ,其中 2\times 2 可以合并为 4(4<7) ,所以可以按照 d = \{4, 5, 5\} 进行 3 次采样,映射函数如上可以写为


(rand4 - 1)\times 25 + (rand5 - 1)\times 5 + (rand5 - 1) + 1

代码如下

// Big to Small, randX->randY
int randB2S(int Y) {
    int res;
    do {
        res = randX();
    } while(res > X - X % Y);
    return (res % Y) + 1;
}

int rand100() {
    int first = randB2S(4);
    int second = randB2S(5);
    int third = randB2S(5);
    return (first - 1) * 25 + (second - 1) * 5 + third;
}

最后: 关于上面的最少采样次数是很难通过一个统一的方法去找到它对应的解,只能根据具体的情况去找,所以是无法设计一个统一的 rand(int Y) 算法并且满足对 Y 最优的。

#面经##春招##暑期实习##计算机##算法#
全部评论
这里讨论的更多的是一种较优性的设计(就是一种直觉上的最优性,因为我不会最优性证明),如果不考虑性能的话,完全可以乱搞,比如例子里的7,100,搞成7*7*7=343的组合,然后再舍弃掉最后的没用的那部分就行了,非常简单。
2
送花
回复
分享
发布于 04-05 19:34 湖北
刘牛牛
点赞
送花
回复
分享
发布于 04-05 18:53 湖北
秋招专场
校招火热招聘中
官网直投
佬强的一
点赞
送花
回复
分享
发布于 04-05 19:30 陕西
佬强的一
点赞
送花
回复
分享
发布于 04-13 23:41 广东
核心思想:拒绝采样
点赞
送花
回复
分享
发布于 04-16 23:26 湖北
太强了吧,这个例子一看就懂了
点赞
送花
回复
分享
发布于 05-16 15:49 四川

相关推荐

头像
05-22 15:20
已编辑
算法工程师
中国科学院软件研究所蔡少伟团队招聘运筹优化实习生运筹优化实习生 岗位职责:1.&nbsp;从事组合优化、图论、数学规划等领域的研究和研发工作;2.&nbsp;为相关工业落地场景提供技术支持,包括数学建模、算法研发等。岗位要求:1.&nbsp;熟悉相关优化算法;2.&nbsp;熟悉常用编程语言;3.&nbsp;优秀的分析和解决问题的能力,对解决具有挑战性的问题充满激情;4.&nbsp;实习至少三个月5.&nbsp;在公司任职实际落地过相关方向项目者优先;6.&nbsp;在顶会发表过相关论文者优先;7.&nbsp;在相关领域竞赛获得优秀名次者优先。薪酬待遇:实习:&nbsp;线下6k~1w/月&nbsp;(远程另议)欢迎私聊或者发送简历至zoumc&nbsp;at&nbsp;ios.ac.cn公司&amp;amp;团队介绍:中科院软件所成立于1985年3月1日,是一所致力于计算机科学理论和软件高新技术的研究与发展的综合性基地型研究所(详情请查阅软件所网页);蔡少伟是软件所计算机科学国家重点实验室博导,曾获得国家优青项目,中科院优秀导师,智源学者,长白山领军人才等荣誉。蔡少伟团队主要从事约束求解、数学规划、EDA相关领域研究,在相关国际比赛多次获得冠军,发表CCF&nbsp;A顶级学术论文70多篇,并获得多个顶级期刊/会议最佳论文、最受欢迎论文等。其技术解决方案在多个工业界领域广泛落地,包括EDA,操作系统验证,云平台优化和检测,工业调度场景等;&nbsp;承担多项国家重大项目课题。
投递中国科学院软件研究所等公司6个岗位
点赞 评论 收藏
转发
10 42 评论
分享
牛客网
牛客企业服务