Python 标准库之 random 生成伪随机数『详细』

在这里插入图片描述

Python 标准库之 random 生成伪随机数


一、Python random介绍🤪

random 模块实现了各种分布的伪随机数生成器,对于整数,从范围中有统一的选择。 对于序列,存在随机元素的统一选择、用于生成列表的随机排列的函数、以及用于随机抽样而无需替换的函数。

本文章内容较多,如果需要查找某个特定的方法或属性,建议使用浏览器的 查找 ctrl + f功能


二、导入 random 库

在看下列内容前,别忘记导入random 标准库呀

import random

三、随机整数

1)、random.randrange(start, stop[, step]):

.randrange(start, stop[, step]) 相当于 .choice(range(start, stop, step)) ,也就是内置函数 range() 和方法 choice() 的结合,但实际上并没有构建一个 range 对象,位置参数模式匹配 range()

演示代码:

res1 = random.randrange(1, 11)          # 1 - 10范围内的随机整数
res2 = random.randrange(1, 11, 2)       # 1 - 10范围内每两个的随机整数
res3 = random.choice(range(1, 11, 2))   # 1 - 10范围内每两个的随机整数
print(res1, res2, res3)

2)、random.randint(a, b):「常用」

a <= N <= b 范围内返回整数N,此方法又相当于 .randrange(a, b+1) ,简单点理解范围即是 range(a, b+1) 然后再从里面随机抽出一个整数
.randrange(start, stop[, step]) 的主要差别是有没有 步长 参数

演示代码:

res1 = random.randint(1, 10)            # 1 - 10范围内的随机整数
res2 = random.randrange(1, 11)          # 1 - 10范围内的随机整数
print(res1, res2)

四、序列用随机函数

1)、random.choice(seq):「常用」

从非空序列 seq 返回 一个 随机元素

演示代码:

res1 = random.choice(range(1, 11))            # 1 - 10范围内的随机整数
res2 = random.choice(["张三", "李四", "王五", "罗翔", "Alex", "Jone"])
print(res1, res2)

如果 seq 为空,则引发 IndexError


2)、random.choices(population, weights=None, cum_weights=None, k=1):

用于有重复的随机抽样,因此不能用作于类似抽奖的用途,返回包含来自 population 序列中元素组成的新列表,新列表的长度以 K 决定,如果需要无重复的随机抽样,参考 .sample() 方法

参数如下:

  • population: population(总体),从population中选择抽取,返回大小为 k 的元素列表

  • weights:如果给出了 weight 序列,则根据相对权重进行选择,如:[10, 5, 30, 5]在进行选择之前相对权重会转化为累计权重

  • cum_weights:如果给出了 cum_weights 序列,则根据绝对权重进行选择,相对权重[10, 5, 30, 5]相当于累积权重[10, 15, 45, 50],直接提供累计权重可以减少 内部代码的工作量

  • k: 随机选取的元素 长度、数量

如果既未指定 weight 也未指定 cum_weights ,则以相等的概率进行选择。 如果提供了权重序列,则它必须与 population 序列的长度相同。

演示代码:

arr = ["张三", "李四", "王五", "罗翔", "Alex", "Jone"]
# 随机选取列表中三元素
res1 = random.choices(arr, k=3)
# 随机选取列表中三元素, Jone 和 罗翔 被选取的概率最大
res2 = random.choices(arr, weights=[5, 10, 30, 25, 15, 30], k=3)
# 随机选取列表中三元素,累计权重概率与上面相对权重相等,Jone 和 罗翔 被选取的概率最大
res3 = random.choices(arr, cum_weights=[5, 15, 45, 70, 85, 115], k=3)
print(res1, res2, res3)

如果 population 为空,则引发 IndexError


3)、random.shuffle(x[, random]):「常用」

shuffle 有着 洗牌 的意思,该方法主要作用是将一个序列中的元素打乱重新排列

源码:

def shuffle(self, x, random=None):
     """Shuffle list x in place, and return None.
     Optional argument random is a 0-argument function returning a
     random float in [0.0, 1.0); if it is the default None, the
     standard random.random will be used.

     """

     if random is None:
         randbelow = self._randbelow
         for i in reversed(range(1, len(x))):
             # pick an element in x[:i+1] with which to exchange x[i]
             j = randbelow(i+1)
             x[i], x[j] = x[j], x[i]
     else:
         _int = int
         for i in reversed(range(1, len(x))):
             # pick an element in x[:i+1] with which to exchange x[i]
             j = _int(random() * (i+1))
             x[i], x[j] = x[j], x[i]

参数如下:

  • x: 将序列 x 随机打乱位置
  • random:可选参数 random 是一个函数,在 [0.0, 1.0) 中返回随机浮点数,如果不指定将默认使用 .random()方法

演示代码:

arr = ["张三", "李四", "王五", "罗翔", "Alex", "Jone"]
print(arr)                  # 打印原数组
random.shuffle(arr)
print(arr)                  # 打印随机后数组

4)、random.sample(population, k): 「常用」

用于无重复的随机抽样,因此能用作于类似抽奖的用途,返回包含来自 population 序列中元素组成的新列表,新列表的长度以 K 决定,如果需要有重复的随机抽样,参考 .choices() 方法

参数如下:

  • population: population(总体),从population中选择抽取,返回大小为 k 的元素列表
  • k: 返回从总体序列或集合中选择的唯一元素的数量、长度

演示代码:

简易抽奖示例

arr = ["张三", "李四", "王五", "罗翔", "Alex", "Jone"]
res = random.sample(arr, 3)
level = {1: "一等奖", 2: "二等级", 3: "三等奖"}
for i, k in enumerate(res, 1):
    print(f"{level[i]}: {k}", end=" ")

如果样本大小大于总体大小,则引发 ValueError


五、实值分布

以下函数生成特定的实值分布。如常用数学实践中所使用的那样, 函数参数以分布方程中的相应变量命名;大多数这些方程都可以在任何统计学教材中找到

1)、random.random():
返回 [0.0, 1.0) 范围内的下一个随机浮点数,精确到小数点后16位

演示代码:

for i in range(10):
    print(round(random.random(), 5))    # 四舍五入精确到后五位

2)、random.uniform(a, b):

返回一个随机浮点数 N ,当 a <= ba <= N <= b ,当 b < ab <= N <= a精确到小数点后16位
简单来说就是返回 a 和 b 之间,包括a 和 b 的一个浮点数,最小范围是 a 和 b 中最小的,最大范围是 a 和 b 中最大的

源码:

源码其实很简单,算法就是公式 a + (b-a) * self.random()

def uniform(self, a, b):
    "Get a random number in the range [a, b) or [a, b] depending on rounding."
    return a + (b-a) * self.random()

演示代码:

res1 = random.uniform(10, 1)                  # 返回 1 <= N <= 10 之间的浮点数
res2 = random.uniform(1, 10)                  # 返回 1 <= N <= 10 之间的浮点数
res3 = 1 + (10-1) * random.random()           # 返回 1 <= N <= 10 之间的浮点数
print(res1, res2, res3, sep=r" // ")

3)、random.triangular(low, high, mode):

返回一个随机浮点数 N ,使得 low <= N <= high 并在这些边界之间使用指定的 mode

源码:

def triangular(self, low=0.0, high=1.0, mode=None):
    """Triangular distribution.
    Continuous distribution bounded by given lower and upper limits,
    and having a given mode value in-between.
    http://en.wikipedia.org/wiki/Triangular_distribution
    """
    u = self.random()
    try:
        c = 0.5 if mode is None else (mode - low) / (high - low)
    except ZeroDivisionError:
        return low
    if u > c:
        u = 1.0 - u
        c = 1.0 - c
        low, high = high, low
    return low + (high - low) * _sqrt(u * c)

参数如下:

  • low: 返回随机浮点数 N 的最小值,默认值为 0
  • high: 返回随机浮点数 N 的最大值,默认值为 1
  • mode: 通过源码可以得知对 mode 的处理 0.5 if mode is None else (mode - low) / (high - low),默认值为 0.5 为边界之间的中点,给出对称分布

演示代码:

arr = ["张三", "李四", "王五", "罗翔", "Alex", "Jone"]
low, high, mode = 0, 5, 5
c = (mode - low) / (high - low)
res1 = random.triangular(low, high)
res2 = random.triangular(low, high, mode=mode)
print(c, res1, res2, sep=r" // ")

4)、random.betavariate(alpha, beta): 「了解」

Beta 分布,又称贝塔分布,参数的条件是 alpha > 0beta > 0, 返回值的范围介于 0 和 1 之间

在这里插入图片描述

概率密度函数

在这里插入图片描述

演示代码:

res1 = random.betavariate(2, 2)
res2 = random.betavariate(0.5, 0.5)
res3 = random.betavariate(5, 1)
print(res1, res2, res3, sep=r" // ")

5)、random.expovariate(lambd): 「了解」

指数分布,该参数本应命名为 lambda ,但这是 Python 中的保留字
如果 lambd 为正,则返回值的范围为 0 到正无穷大;如果 lambd 为负,则返回值从负无穷大到 0

演示代码:

res1 = random.expovariate(1)
res2 = random.expovariate(-1)
print(res1, res2, sep=r" // ")

6)、random.gammavariate(alpha, beta): 「了解」

Gamma 分布,参数的条件是 alpha > 0beta > 0

概率分布公式是:👇🏻
在这里插入图片描述
演示代码:

res1 = random.gammavariate(5, 1)
res2 = random.gammavariate(1, 6)
res3 = random.gammavariate(6, 6)

print(res1, res2, res3, sep=r" // ")

7)、random.gauss(mu, sigma): 「了解」

高斯分布, mu 是平均值,sigma 是标准差,这比下面要讲的 normalvariate() 函数略快

正态分布(台湾作常态分布,英语:normal distribution)又名高斯分布(英语:Gaussian distribution)、正规分布,是一个非常常见的连续概率分布,正态分布在统计学上十分重要,经常用在自然和社会科学来代表一个不明的随机变量

在这里插入图片描述

概率密度函数
红线代表标准正态分配

演示代码:

res1 = random.gauss(0, 0.2)
res2 = random.gauss(0, 1)
res3 = random.gauss(0, 5)
res4 = random.gauss(-2, 0.5)
print(res1, res2, res3, res4, sep=r" \\ ")

8)、random.lognormvariate(mu, sigma): 「了解」

任意随机变量的对数服从正态分布,则这个随机变量服从的分布称为对数正态分布,如果你采用这个分布的自然对数,你将得到一个正态分布
平均值为 mu 和标准差为 sigma,mu 可以是任何值,sigma 必须大于零

在这里插入图片描述

μ=0 概率密度函数

演示代码:

res1 = random.lognormvariate(0, 10)
res2 = random.lognormvariate(0, 1)
res3 = random.lognormvariate(0, 0.5)
res4 = random.lognormvariate(0, 0.25)
print(res1, res2, res3, res4, sep=r" \\ ")

9)、random.normalvariate(mu, sigma): 「了解」

正态分布,mu 是平均值,sigma 是标准差,具体参考 7)、random.gauss(mu, sigma)

演示代码:

res1 = random.normalvariate(0, 0.2)
res2 = random.normalvariate(0, 1)
res3 = random.normalvariate(0, 5)
res4 = random.normalvariate(-2, 0.5)
print(res1, res2, res3, res4, sep=r" \\ ")

10)、random.vonmisesvariate(mu, kappa): 「了解」

冯·米塞斯(von Mises)分布,mu 是平均角度,以弧度表示,介于0和 2*pi 之间,kappa 是浓度参数,必须大于或等于零
如果 kappa 等于零,则该分布在 0 到 2*pi 的范围内减小到均匀的随机角度

演示代码:

res1 = random.vonmisesvariate(6, 0)
res2 = random.vonmisesvariate(6, 1)
res3 = random.vonmisesvariate(6, 6)
res4 = random.vonmisesvariate(90, 0)
print(res1, res2, res3, res4, sep=r" \\ ")

11)、random.paretovariate(alpha): 「了解」

帕累托分布(Pareto distribution)是以意大利经济学家维尔弗雷多·帕累托命名的,alpha 是形状参数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

概率密度函数

演示代码:

res1 = random.paretovariate(1)
res2 = random.paretovariate(2)
res3 = random.paretovariate(3)
print(res1, res2, res3, sep=r" \\ ")

12)、random.weibullvariate(alpha, beta): 「了解」

威布尔分布(Weibull distribution)是可靠性分析和寿命检验的理论基础,alpha 是比例参数,beta 是形状参数
在这里插入图片描述
在这里插入图片描述

概率密度函数

演示代码:

res1 = random.weibullvariate(1, 0.5)
res2 = random.weibullvariate(1, 1)
res3 = random.weibullvariate(1, 1.5)
res4 = random.weibullvariate(1, 5)
print(res1, res2, res3, res4, sep=r" \\ ")

六、random 可以做什么?🤔

random 的中文释义是 随机的 意思,那顾名思义干的就是一些随机的活

1)、抽个奖压压惊

抽奖类问题是 随机 最常见的实际应用之一,在上面的方法演示中也提到过了抽奖

a. 先来看看常见思维的写法,循环需要的奖项,在每次循环中将其进行抽取各奖项的获奖者

def get_winners(items: list, level: dict):
    """
    抽取获奖者传统思维写法
    :param items: 参与人员列表
    :param level: 获奖级别信息
    :return: 获奖结果
    """
    res = {}
    for i in level.keys():
        item = random.sample(arr, level[i])        # 随机选取指定数量的元素
        for d in item:                             # 遍历选取结果并删除
            items.remove(d)
        res[i] = item                              # 添加到结果字典中
    return res


arr = ["张三", "李四", "王五", "罗翔", "Alex", "Jone", "Mark", "李华", "全蛋", "铁花"]
win_level = {"一等奖": 1, "二等奖": 3, "三等奖": 5}
print(get_winners(arr, win_level))

b. 在上面介绍过random.sample(population, k)方法,它是无重复随机抽样,运用这个无重复一次性把所有获奖抽出,再以前后顺序确定奖项即可

def get_winners(items: list, level: dict):
    """
    抽取获奖者以索引取法
    :param items: 参与人员列表
    :param level: 获奖级别信息
    :return: 获奖结果
    """
    target = random.sample(items, sum(level.values()))
    res, prev = {}, 0
    for i in level.keys():
        res[i] = target[prev: level[i] + prev]
        prev += level[i]
    return res


arr = ["张三", "李四", "王五", "罗翔", "Alex", "Jone", "Mark", "李华", "全蛋", "铁花"]
win_level = {"一等奖": 1, "二等奖": 3, "三等奖": 5}
print(get_winners(arr, win_level))

2)、猜号数

学习 Python 的同学很多都有接触过这个题目,猜号数问题算是最基础的 Python题目之一,这里咱就复习一下

a. 生成一个 1~100之间的随机数,判断用户输入的数值是否与生成的随机数想等,如果大于这个值或小于这个值,应当提醒用户

其中 c_print() 函数作用用于颜色打印,详情可以参考我关于 颜色打印 的文章

def fuess_the_number():
    """
    猜数字小游戏, 无限制次数
    """
    target = random.randint(1, 100)						# 生成需要猜的目标值
    while True:
        enter = input("请输入需要您所猜的值:")
        if not enter.isdigit():							# 判断是否为数字
            c_print("请输入整数", color=31)
        elif not (100 >= (enter := int(enter)) >= 1):	# 判断范围是否在 1-100 之间
            c_print("请输入100-1之间的值", color=31)
        else:
            if enter > target:
                c_print("数值太大啦[再试试]", color=36)
            elif enter < target:
                c_print("数值太小啦[再试试]", color=36)
            else:
                c_print("Bingo, 答对了")
                break

b. 在上述问题的基础上,自定义最大可尝试次数,更改量其实很少

def fuess_the_number(max_try: int):
    """
    猜数字小游戏, 无限制次数
    """
    target = random.randint(1, 100)					    # 生成需要猜的目标值
    for _ in range(max_try):
        enter = input("请输入需要您所猜的值:")
        if not enter.isdigit():							# 判断是否为数字
            c_print("请输入整数", color=31)
        elif not (100 >= (enter := int(enter)) >= 1):	# 判断范围是否在 1-100 之间
            c_print("请输入100-1之间的值", color=31)
        else:
            if enter > target:
                c_print("数值太大啦[再试试]", color=36)
            elif enter < target:
                c_print("数值太小啦[再试试]", color=36)
            else:
                c_print("Bingo, 答对了")
                break
    else:
        c_print("很遗憾您并没有猜出来, 挑战结束", color=36)


fuess_the_number(5)

在限定次数中,我将错误检测也算进了可猜次数中,这里我留下这个坑,有兴趣的同学可以自己去试试做出更改

3)、发个牌

相信大多数人都打过扑克牌吧,我就没打过🙄写这之前还去查了一下组合发牌类问题是 随机 最常见的实际应用之一,大家可以先去尝试编写一个利用 random 随机发牌的小程序

52张正牌又均分为13张一组,并以黑桃、红桃、梅花、方块四种花色表示各组,每组花色的牌包括从1-10(1通常表示为A)以及J、Q、K标示的13张牌

  • 花色: ["梅花", "方片", "红桃", "黑桃"]
  • 牌面数字: ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
class Card:
    def __init__(self):
        self.card_color = ["梅花", "方片", "红桃", "黑桃"]
        self.card_nums = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
        self.cards = self.init_card()      # 获取初始牌组合

    def init_card(self):
        """
        初始牌组合方法
        :return: 组合牌
        """
        cards = []
        for i in self.card_color:
            cards.extend([f"{i}{k}" for k in self.card_nums])
        return cards

    def shuffle_card(self, shu_num=1):
        """
        洗牌方法
        :param shu_num: 洗牌次数, 默认为1
        :return: 洗后牌
        """
        for _ in range(shu_num):
            random.shuffle(self.cards)
        return self.cards

    def deal_card(self, items: list or tuple):
        """
        发牌方法
        :param items: 玩家成员 
        :return: 每个成员所发的牌
        """
        if not isinstance(items, (list, tuple)):
            raise TypeError("成员信息应以列表或元组传入")
        elif (length := len(items)) > 4:
            raise Exception("成员过多")
        dic, prev, random_res = {}, 0, random.sample(self.cards, length*13)
        for i in items:
            # prev 为索引指针
            dic[i] = random_res[prev:prev+14]
            prev += 14
        return dic
play = Card()                   # 实例化游戏对象
play.shuffle_card(5)            # 洗牌5次
player = play.deal_card(        # 添加玩家发牌
    ["张三", "罗翔"]
)
{'张三': ['梅花7', '黑桃K', '红桃8', '方片A', '方片7', '梅花2', '梅花5', '黑桃2', '方片3', '红桃A', '梅花6', '红桃5', '黑桃9', '黑桃4'], 
'罗翔': ['红桃2', '红桃Q', '梅花3', '红桃6', '红桃J', '黑桃A', '方片8', '梅花4', '方片5', '黑桃5', '红桃K', '梅花Q']}

4)、画个雪花玩玩 「大型运用」

说到画图,印象较深的就是Python的海龟库 turtle,这是大多数Python学习者初期所接触到的标准库,我将使用 turple 进行图像绘画
画雪花使用 random 的次数绝对够多,雪花的大小,雪花的粗细,雪花的位置,这些都是需要考虑随机的东西,因此可以在示例代码中能够见到多次random的身影

在这里插入图片描述

def draw_snow(min_size, max_size, color=True):
    """
    绘画雪花函数
    :param min_size: 最小雪花长度
    :param max_size: 最大雪花长度
    :param color: 是否随机颜色, 默认为是
    """
    t.pendown()
    if color is True:
        # 颜色模式改成255, 并随机rgb颜色
        t.colormode(255)
        rgb = [random.randint(0, 255) for _ in range(3)]
        t.pencolor((rgb[0], rgb[1], rgb[2]))
    else:
        t.pencolor("white")
    snowsize = random.randint(min_size, max_size)
    side = snowsize // 2                    # 边数
    for _ in range(side):
        t.forward(snowsize)
        t.backward(snowsize)
        t.right(360 / side)
    t.penup()


def draw(quantity, x, y):
    """
    主画图
    :param quantity: 雪花数量
    :param x: 窗口宽度大小
    :param y: 窗口高度大小
    """
    t.ht()                                  # 隐藏海龟
    t.setup(x, y, 500, 250)                 # 设置窗口大小
    t.bgcolor("black")                      # 设置背景色
    t.tracer(False)                         # 关闭海龟动画
    t.title("雪花图")
    for _ in range(quantity):
        t.penup()
        # 获取x轴与y轴雪花范围
        x_range = random.randint(-(x//2-50), (x//2-50))
        y_range = random.randint(-(y//2-50), (y//2-50))
        t.pensize(random.randint(1, 3))     # 1-3 画笔随机大小
        t.goto(x_range, y_range)            # 移动画笔
        draw_snow(15, 30)                   # 调用画雪花
    t.exitonclick()                         # 鼠标点击后关闭窗口


if __name__ == '__main__':
    draw(100, 800, 500)

扩展:Python的集合也能实现随机

集合的 .pop() 方法

从集合中移除并返回任意一个元素,其实就是随机移除一个元素并返回
如果集合为空则会引发 KeyError,可以先判断集合是否为空

需要注意的是,集合的 .pop 方法的随即效果只针对字符串有效,如果是数字类型每次移除并返回的都是同一个元素,详情请移步我关于集合的文章

还是以随机抽奖为例👇🏻

s_user = {"张三", "李四", "小美", "王华", "小明", "李华"}
awarded = []
while len(awarded) < 3:
    awarded.append(s_user.pop())
print(f"获奖用户: {' '.join(awarded)}")

扩展:为什么说 random 是伪随机数

的近义词是 ,有着假的意思,那么 伪随机数 又指的是什么意思呢?

我们先来看看官方手册对 伪随机数 的描述:👇🏻

Python 使用 Mersenne Twister 作为核心生成器。 它产生 53 位精度浮点数,周期为 2**19937-1 ,其在 C 中的底层实现既快又线程安全。 Mersenne Twister 是现存最广泛测试的随机数发生器之一。 但是,因为完全确定性,它不适用于所有目的,并且完全不适合加密目的警告 不应将此模块的伪随机生成器用于安全目的。 有关安全性或加密用途,请参阅 secrets 模块。,在 random 手册下另外还有这一句话,看来想要知道这个伪是什么意思,我们得进一步的查看 secrets 标准库模块下的手册

secrets 手册下 又有着这么一句话 特别地,应当优先使用 secrets 来替代 random 模块中的默认伪随机数生成器,后者被设计用于建模和仿真,而不适用于安全和加密。

如果好奇心爆棚,可以去看看 PEP 506 对此的描述,简单来说使用 random 随机的结果是有一定规律可循的,有一定漏洞的随机数


参考资料💌

由衷感谢💖


相关博客😏

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值