將生成1-5隨機(jī)數(shù)函數(shù)轉(zhuǎn)換為1-7隨機(jī)數(shù)函數(shù)的實(shí)現(xiàn)方法

個人技術(shù)博客地址:http://songmingyao.com/


問題

現(xiàn)有一個生成1-5隨機(jī)數(shù)的函數(shù)random_5(概率平均),如何將此轉(zhuǎn)換為生成1-7隨機(jī)數(shù)random_7的函數(shù)(概率平均),不得使用random模塊

思路一(False)

直接將random_5生成的隨機(jī)數(shù)乘以7/5后再通過round函數(shù)進(jìn)行四舍五入后輸出

random_5輸出 random_7輸出
1 1
2 3
3 4
4 6
5 7

特別糟糕的想法...

思路二(False)

生成兩次random_5,將兩者的值相加后減一(random_5 + random_5 - 1),得出隨機(jī)數(shù)1-9,而后再做一次判斷,將8和9剔除掉,只在返回1-7的時候輸出

不過很快就發(fā)現(xiàn),只有在兩次random_5都返回1的時候,才會返回1,概率遠(yuǎn)小于返回其它六個數(shù)

又是一個糟糕的想法...

思路三(True)

  • 通過之前的兩種思路,可以總結(jié)出將random_5返回的數(shù)據(jù)經(jīng)過各種處理后不篩選返回1-7是不可能實(shí)現(xiàn)的(不能說不可能,只是我個人能力有限,暫時還沒想出方法),那必須采用思路二中的篩選方法。
  • 確定要使用篩選方法之后,目標(biāo)就變成了生成等概率的、值取值范圍大于1-7的隨機(jī)數(shù)。
  • 相加和返回值乘系數(shù)的方法都嘗試過了,那么是否可以將兩者結(jié)合一下呢。
  • 首先思考乘系數(shù),random_5 * 2 - 1的返回結(jié)果是[1, 3, 5, 7, 9],想要等概率生成1-10的話,比如再將此返回值加上隨機(jī)生成的0和1,也就是random.randint(0, 1)
  • 因不允許使用random模塊,所以我們必須想辦法重復(fù)利用原有的random_5函數(shù),random_5 * 3 - 2需要對應(yīng)的random.randint(0, 2),也可以理解為random.randint(1, 3) - 1
  • 此時,顯而易見,random_5 * 5 - 4需要對應(yīng)的random.randint(1, 5) - 1,也就是random_5 - 1,那么基本的函數(shù)體就出來了,就是
random_5 * 5 + random_5 - 5
  • 再將篩選的邏輯加進(jìn)去,完整代碼如下:
import random


# 原有生成1-5隨機(jī)數(shù)的函數(shù)
def random_5():
    return random.randint(1, 5)


# 要獲得的生成1~7隨機(jī)數(shù)的函數(shù)
def random_7():
    while True:  # 避免沒有返回值
        n = (random_5()-1)*5 + random_5()-1  # 生成0~24的隨機(jī)數(shù)
        if n <= 20:
            return n % 7 + 1


if __name__ == '__main__':
    print(random_7())

進(jìn)階

剛才得出的核心代碼,去掉常變量之后就變成了random_5 * 5 + random_5,看著很像二、八、十六進(jìn)制轉(zhuǎn)換為十進(jìn)制的方法,也就是可以理解為五進(jìn)制,通過這種思想我們可以得出一個通用的隨機(jī)數(shù)轉(zhuǎn)換方法,具體代碼如下:

import random
import math


class RandomIJToRandomAB(object):
    """將原來i-j之間的隨機(jī)數(shù)生成函數(shù),轉(zhuǎn)化為a-b之間的隨機(jī)數(shù)生成函數(shù)"""
    def __init__(self, i, j):
        self.i = i
        self.j = j
        ij_dif = j-i

        if ij_dif <= 0:
            raise ValueError

        self.ij_dif = ij_dif

    def random_ij(self):
        return random.randint(0, self.ij_dif)

    def random_ab(self, a, b):
        ab_dif = b-a
        digit = int(math.ceil(math.log(ab_dif+1, self.ij_dif+1)))  # 獲取差值(ab_dif)轉(zhuǎn)換為ij_dif+1進(jìn)制后的最大位數(shù)

        while True:  # 防止return None
            # 生成最大位數(shù)為digit的ij_dif+1進(jìn)制隨機(jī)數(shù),而后轉(zhuǎn)化為10進(jìn)制
            random_res = 0
            for item in range(digit):
                random_res += self.random_ij() * pow(self.ij_dif+1, item)

            # 只取在區(qū)間范圍內(nèi)的隨機(jī)數(shù)
            if random_res <= ab_dif:
                return random_res+a


if __name__ == '__main__':
    print('0~1隨機(jī)生成函數(shù)轉(zhuǎn)化為2~11隨機(jī)生成函數(shù)')
    random_1 = RandomIJToRandomAB(0, 1)
    print(random_1.random_ab(2, 11))
    print('-'*50)

    print('1~5隨機(jī)生成函數(shù)轉(zhuǎn)化為1~7隨機(jī)生成函數(shù)')
    random_1_5 = RandomIJToRandomAB(1, 5)
    print(random_1_5.random_ab(1, 7))
    print('-'*50)

    print('1~7隨機(jī)生成函數(shù)轉(zhuǎn)化為1~5隨機(jī)生成函數(shù)')
    random_1_7 = RandomIJToRandomAB(1, 7)
    print(random_1_7.random_ab(1, 5))
    print('-'*50)

Github源碼地址:

random5_to_random7:
https://github.com/shelmingsong/algorithm_practice/blob/master/31_random5_to_random7_20170824.py

random_ij_to_random_ab:
https://github.com/shelmingsong/algorithm_practice/blob/master/32_random_ij_to_random_ab_20170824.py

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容