[路飞] 用 rand7() 实现 rand10()

116 阅读2分钟

记录 1 道算法题

用 rand7() 实现 rand10()

470. 用 Rand7() 实现 Rand10() - 力扣(LeetCode) (leetcode-cn.com)


rand7 是一个返回 [1,7] 的随机数函数。利用 rand7 实现随机数 [1, 10]。概率要公平。

这道题用到的数学公式是 (randX - 1) * Y + randY

就是说 randX - 1 行, Y 列的一个矩形。然后第 randX 行的 randY 个。

        1  2  3  4  5
        6  7  8  9  10
        11 12 13
        
        这里就是有 (3 - 1) * 5 的一个矩形,加最后一行 3 个。

这个公式正好能根据 randX 和 randY 两个随机数决定一个 Y 列的顺序排列的数字。概率相等的抽到里面的每一个数字。

在这道题里面使用的是:(rand7() - 1) * 7 + rand7() 就可以公平的取到 [1, 49] 的数字。

剩下的就是只要在 10 的倍数的时候返回取余计算后的结果就可以得到 [1, 10] 的随机数。

    function rand10() {
        let a,b
        do {
            a = rand7()
            b = rand7()
            因为 [1, 49] 最大的 10 的倍数是 40,所以只能是40以内
            a = (a - 1) * 7 + b
        } while (a > 40)
        
        return 1 + a % 10
    }

有 9 / 49 的概率是要再进行一次循环的,那如何把概率减少,让程序更高效?

在 7 的倍数里有 0,7,14,21,28,35,42,49。如果不是再做一次循环而是加一个 else,则会得到 [40,49] 的随机数,如果减去40的话,则得到 [1,9] 的随机数。前面说了有一个 x 决定多少行。如果把这个随机数作为 x,可以得到一个新的矩形。最少 1 行,最多 9行,所以就会得到一个 [1, 63] 的随机数。如果再多出来,这时候多出来的数就是 [1,3]。随机数的范围就是 [1, 21]。21 非常接近 20,而且只做了3次循环。随机到 21 的概率还是很少的,如果随机到了就从 [1, 49] 开始就行了。

    function rand10() {
        while(true) {
            let a = rand7()
            a = (a - 1) * 7 + rand7()
            if (a <= 40) break
            
            a = (a - 40 - 1) * 7 + rand7()
            if (a <= 60) break
            
            a = (a - 60 - 1) * 7 + rand7()
            if (a <= 20) break
        }
        
        return 1 + a % 10
    }