master
/ 5.3 random 模块及其应用.ipynb

5.3 random 模块及其应用.ipynb @masterview markup · raw · history · blame

Notebook

random模块及其应用

5.14 random模块及应用.jpg

Python中使用random模块产生各种分布的伪随机数。
使用随机数函数时,直接导入random模块即可,方法如下:

import random import math

随机数生成器(种子)

“种子”是这个算法开始计算的第一个值。
如果随机数种子一样,那么后续所有“随机”结果和顺序也都是完全一致的。
Python语言不设置随机数种子时,解释器会使用系统时间作为种子,使每次生成的随机数不同。
当希望得到的随机数据可预测,可以设置用相同的种子,使后续产生的随机数相同。

random.seed(a=None, version=2) # 随机数种子

seed 必须是下列类型之一:
NoneType、int、float、str、bytes或bytearray。

In [6]:
import random

# 不指定随机数种子时,每次产生的序列是不同的。
for i in range(5):
	print(random.randint(1, 100), end=' ')
print()
for i in range(5):
	print(random.randint(1, 100), end=' ')
print()
56 69 25 11 66 
90 17 14 8 96 
In [5]:
import random

# 用相同的随机数种子可以得到相同的序列
random.seed(10)  # 用整数10做种子
for i in range(5):
	print(random.randint(1, 100), end=' ')
print()

random.seed(10)  # 用整数10做种子
for i in range(5):
	print(random.randint(1, 100), end=' ')
函数 描述与示例
random.seed() 初始化随机数生成器,缺省时用系统时间做种子。seed 必须是下列类型之一: NoneType、int、float、str、bytes或bytearray。
random.randint(a, b) 产生[a,b]之间(包括b)的一个随机整数
random.random() 产生[0.0,1.0)之间的一个随机浮点数
random.uniform(a, b) 产生[a, b]之间的一个随机浮点数
random.choice(seq) 从非空序列seq中随机产生一个元素,当序列为空时,触发索引异常
random.choices(population, weights=None, *, _cumweights=None, k=1) population中选择替换,返回大小为 k 的元素列表。k个元素可以重复
random.shuffle(x[,random]) 将可变序列x顺序打乱
random.sample(population, k) 从列表、元组、字符串、集合、range对象等分布式序列中随机选取k个元素,不重复取, 以列表形式返回。

random模块的sample和choices函数都可以实现随机抽样:
sample实现无放回抽样,这意味着抽样取出的元素是不重复的;
choices实现有放回抽样,这意味着可能会重复选中某些元素。

这两个函数的第一个参数代表抽样的总体,而参数k代表样本容量,需要说明的是choices函数的参数k是一个命名关键字参数,在传参时必须指定参数名。

In [3]:
import random
import string

ALL_CHARS = string.digits + string.ascii_letters
 

def generate_code(*, code_len=4):
    """
    生成指定长度的验证码
    :param code_len: 验证码的长度(默认4个字符)
    :return: 由大小写英文字母和数字构成的随机验证码字符串
    """
    return ''.join(random.choices(ALL_CHARS, k=code_len))  # k是默认值参数,使用时需要用k=表示

# 调用函数,生成默认长度的验证码
for i in range(5):
    print(generate_code())

print('字符串中可能存在重复字符')  # 例如:gEeGrg,SgVjgS

# 调用函数,生成指定长度的验证码
for i in range(5):
    print(generate_code(code_len=6))
hiSw
F7kf
nbsu
gFtP
FIVk
字符串中可能存在重复字符
3puFUn
u9NbeF
VL94Tp
UJr2GO
u06Z9L
In [7]:
import random
import string

ALL_CHARS = string.digits + string.ascii_letters
 

def generate_code(*, code_len=4):
    """
    生成指定长度的验证码
    :param code_len: 验证码的长度(默认4个字符)
    :return: 由大小写英文字母和数字构成的随机验证码字符串,无重复字符
    """
    return ''.join(random.sample(ALL_CHARS, code_len)) # k是位置参数,使用时直接传入数值

# 调用函数,生成默认长度的验证码
for i in range(5):
    print(generate_code())

print()
# 调用函数,生成指定长度的验证码
for i in range(5):
    print(generate_code(code_len=6))

模拟双色球随机选号

In [ ]:
import random

RED_BALLS = [i for i in range(1, 34)]  # 从1到33中随机选取6个数字作为红色球的号码
BLUE_BALLS = [i for i in range(1, 17)]  # 从1到16中随机选取1个数字作为蓝色球的号码

def choose():
    """
    生成一组随机号码
    :return: 保存随机号码的列表
    """
    selected_balls = random.sample(RED_BALLS, 6)  # 从RED_BALLS中随机选取6个元素
    selected_balls.sort()  # 对列表中的元素进行排序
    selected_balls.append(random.choice(BLUE_BALLS))  # 从BLUE_BALLS中随机选取1个元素并追加到列表中
    return selected_balls  # 返回生成的随机号码

def display(balls):
    """
    格式输出一组号码
    :param balls: 保存随机号码的列表
    """
    for ball in balls[:-1]:
        print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
    print(f'\033[034m{balls[-1]:0>2d}\033[0m')

n = int(input('生成几注号码: '))
for i in range(n):
    display(choose())

用于字节数据的函数

random.randbytes(n) # 生成 n 个随机字节
In [4]:
import random

s = random.randbytes(6)
print(s)  # b'bT\xc9@\xaf\xd8'

生成随机整数函数

random.randint(a, b) # 产生[a,b]之间(包括b)的一个随机整数
In [4]:
import random

print(random.randint(0, 128))  # 随机产生一个0-128之间的整数
103
random.randrange(stop) # 从[0-stop) (不含stop)中随机产生一个整数random.randrange(start, stop[, step]) # 从[start-stop),步长为step的序列里随机产生一个整数 相当于 choice(range(start, stop, step)) ,但实际上并没有构建一个 range 对象。

实例 随机产生一个身份证顺序号

身份证号码第十五位到第十七位为顺序码,是县级公安机关所辖派出所的分配码,每个派出所分配码为10个连续号码,例如“000-009”或“060-069”,其中奇数为男性分配码,偶数为女性分配码。随机产生一个身份证顺序号。

1. 先用random.randrange(100)产生一个两位整数,不足2位时前面要补0 2. 根据性别产生一个奇数或一个偶数。 偶数可以用random.randrange(0, 10, 2) 来产生,奇数可以用random.randrange(1, 10, 2) 来产生 3. 将两个整数拼接为一个字符串
In [8]:
import random

order = random.randrange(100)               # 2位整数
gender = random.randrange(0, 10, 2)         # 偶数
print(f'{(str(order) + str(gender)):>03}')  # 060,不足2位补0
print(f'{order:02}{gender}')                # 060,不足2位补0
random.getrandbits(k) # 返回具有 k 个随机比特位的非负 Python 整数

实例 随机产生一个IP地址

IP地址是互联网上的每一个网络和每一台主机分配一个逻辑地址, 这是一个32位的二进制数,通常被分割为4个“8位二进制数”。 IP地址通常用“点分十进制”表示, 例:点分十进IP地址 100.4.5.6, 实际上是32位二进制数01100100.00000100.00000101.00000110 这样的数字正好可以用getrandbits(k) 函数产生。

In [9]:
import random
 
dot = '.'                      # 定义一个用于分隔符的变量
for i in range(4):
    n = random.getrandbits(8)  # 每次产生一个8个二进制位对应的整数
    if i == 3:
        dot = ''               # 若是最后一个数,分隔符修改为空字符串
    print(n, end=dot)  # 241.26.4.219
In [10]:
import random
 
dot = '.'                      # 定义一个用于分隔符的变量
for i in range(4):
    n = random.randint(0, 255)  # 每次产生一个0-255的整数
    if i == 3:
        dot = ''               # 若是最后一个数,分隔符修改为空字符串
    print(n, end=dot)  # 241.26.4.219

序列用函数

random.choice(seq) # 从非空序列seq中随机产生一个元素,当序列为空时,触发索引异常
In [11]:
import random

print(random.choice(['win', 'lose', 'draw']))  # 输出'draw'
In [12]:
pwd = ''
for i in range(8):
	pwd = pwd + random.choice('')  # 序列为空,IndexError: string index out of range
print(pwd)  

实例 产生数字密码

学校的教务系统每个学期都会随机产生一个8位数字的登分密码,编程实现。

In [13]:
# 随机产生8个数字组成一个密码
import random

pwd = ''                                      # 定义一个空字符串
for i in range(8):                           # 重复8次
	pwd = pwd + random.choice('0123456789')  # 每次随机产生一个数字字符,拼接到字符串上
print(pwd)  # 64295542

random.choices(population, weights=None, *, cum_weights=None, k=1)
从population中选择,返回大小为 k 的元素__列表__
既未指定 weight 也未指定 cum_weights ,则以__相等的概率__进行选择

In [14]:
import random

pwd = random.choices('0123456789', k=8)
print(pwd)           # ['5', '1', '9', '8', '6', '7', '2', '0']
print(''.join(pwd))  # 用join()拼接为一个字符串,51986720
random.shuffle(x[,random]) # 将可变序列x顺序打乱
In [15]:
import random

poker = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
print(poker)
random.shuffle(poker)  # 原地改变可变序列的顺序,函数无返回值
print(poker)

x必须为__可变序列__,如列表。 原地改变可变序列的顺序,函数__无返回值__

In [17]:
import random

poker = 'A23456789JQK'  # 字符串是不可变数据,不能用shuffle()
random.shuffle(poker)
print(poker)
random.sample(population, k, *, counts=None)

从列表、元组、字符串、集合、range对象等分布式序列中随机选取k个唯一元素,k不超过序列长度(k<=len(population)),用于无重复的随机抽样,以__列表__形式返回。
在将来,population 必须是一个序列。
__set 的实例将不再被支持__。 集合必须先转换为 list 或 tuple,最好是固定顺序以使抽样是可重现的。

In [18]:
import random

print(random.sample([10, 20, 30, 40, 50], k=4))  # 输出 [50, 10, 20, 30]
In [19]:
import random


pwd = random.sample('0123456789', k=8)  # 返回值为列表 ['9', '8', '6', '5', '3', '1', '7', '0']
print(pwd)
print(''.join(pwd))                     # 序列连接为一个字符串98653170
In [20]:
import random

digit = '0123456789'
pwd = random.sample('0123456789', k=len(digit))  # 打乱不可变序列,返回列表
print(pwd)             # ['9', '3', '1', '4', '0', '6', '5', '8', '2', '7']
print(''.join(pwd))    # 序列连接为一个字符串9314065827
重复的元素可以一个个地直接列出,或使用可选的仅限关键字形参 counts 来指定。
In [24]:
import random


pwd1 = random.sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)
pwd2 = random.sample(['red', 'blue'], counts=[4, 2], k=5)  # 'red'重复4次, 'blue'重复2次
print(pwd1)
print(pwd2)
如果样本大小大于总体大小,则引发 ValueError
In [22]:
import random

print(random.sample([10, 20, 30, 40, 50], k=6))  # 样本大小大于总体大小,引发 ValueError

实例 5.10 模拟校验验证码

用户在网络上注册或登录各平台时,经常需要输入验证码。这些验证码采取随机生成的方式产生,包含大小写字母和数字。用户输入验证码时,一般不区分大小写,请编写程序对用户的输入的验证码进行验证。 用户输入时不区分大小写,在验证前可以将用户输入的字符串和验证码中的大写字母都转为小写字母;或反过来,将所有小写字母转换为大写字母,再进行匹配验证。
In [25]:
import random                  # 导入随机数模块
import string                  # 导入字符串模块
 
# sample()方法从包含字母与数字的字符串中随机获取6个元素,返回列表
code = random.sample(string.ascii_letters+string.digits, 6)
print(code)                    # ['v', '8', 'G', 'Z', 'w', '7']
vcode = ''.join(code).upper()  # 列表code元素连接为字符串,字母转大写
print(vcode)                   # V8GZW7
check_code = input().upper()   # 将输入中的字母转为大写
if check_code == vcode:        # 将输入和产生的验证码进行比较
    print('验证码正确')
else:
    print('验证码错误,请重新输入')

实例 5.11 模拟微软序列号

微软产品一般都一个25位的、用于区分每份微软产品的产品序列号。产品序列号由五组被“-”分隔开,由字母数字混合编制的字符串组成,每组字符串是由五个字符串组成。例如: 3CVX3-BJWXM-6HCYX-QEK9R-CVG4R 每个字符是取自于以下24个字母及数字之中的一个: B C E F G H J K M P Q R T V W X Y 2 3 4 6 7 8 9 采用这24个字符的原因是为了避免混淆相似的字母和数字,如I 和1,O 和0等,减少不必要的麻烦。
In [26]:
import random                          # 导入随机数模块
 
keySn = ''                            # 创建一个空字符串,容纳序列号
code = 'BCEFGHJKMPQRTVWXY2346789'      # 限定字符集合
for i in range(5):
    s = ''                             # 创建空字符串,容纳序列号一节
    for j in range(5):                 # 每次产生1个字符,5字符一组
        s = s + random.choice(code)    # 产生一个字符,拼接在字符串后
    if i == 0:                         # 判断是否为第一个字符串
        keySn = keySn + s              # 直接拼接在字符串keySn上
    else:
        keySn = keySn + '-' + s        # 用“-”拼接字符串
print(keySn)                          # EPMQW-RJFBR-T99EG-CT968-XE9PR
In [27]:
import random                      # 导入随机数模块
 
keySn = []                        # 创建一个空列表,用于容纳序列号
code = 'BCEFGHJKMPQRTVWXY2346789'  # 字符集合
for i in range(5):
    s = random.sample(code, 5)     # 从code中随机取5个字符
    keySn.append(''.join(s))       # 将5个字符连接为字符串附加到列表
print(keySn)          # ['WCMRQ', 'Y4MCR', 'BGPJ6', '3KPB9', '23RGQ']
print('-'.join(keySn))# 用‘-’将列表中的元素连接起来

算法1:蒙特卡洛模拟

蒙特卡罗法的基本思想是: 为了求解问题,首先建立一个概率模型或随机过程,使它的参数或数字特征等于问题的解:然后通过对模型或过程的观察或抽样试验来计算这些参数或数字特征,最后给出所求解的近似值。解的精确度用估计值的标准误差来表示。 蒙特卡罗法 的主要理论基础是概率统计理论,主要手段是随机抽样、统计试验。 用蒙特卡罗法求解实际问题的基本步骤为: (1)根据实际问题的特点.构造简单而又便于实现的概率统计模型.使所求的解恰好是所求问题的概率分布或数学期望; (2)给出模型中各种不同分布随机变量的抽样方法; (3)统计处理模拟结果,给出问题解的统计估计值和精度估计值。

示例5.12 蒙特卡洛法解百钱百鸡

In [3]:
import random


def monte_carlo_cock(num):
    answer = []
    for i in range(num):
        hen = random.randint(1, 20)
        cock = random.randint(1, 33)
        chicken = 100 - cock - hen
        if chicken % 3 == 0 and 5 * cock + 3 * hen + chicken // 3 == 100 and cock + hen + chicken == 100:
            group = [cock, hen, chicken]
            if group not in answer:
                answer.append(group)
    return answer


if __name__ == '__main__':
    result = monte_carlo_cock(5000)
    print(result)

示例5.13 赌徒博弈

如果赌博输赢的概率都是50%,为什么长久赌博的人多会倾家荡产? 设定: · 设定我们假设有 1000 个玩家,每人手里有 10000 元的筹码,可以用来赌博。 · 每一局游戏,玩家都可以抵押 500 元的筹码。输了,筹码不退还;赢了,筹码翻倍。 · 输赢的概率各占 50%。 · 所有玩家一共进行 100 局游戏。一旦任何玩家筹码为0,必须中途退出游戏。

In [22]:
# 利用以下代码分别测试每次押注500-3000时的输赢情况,会发现当玩的局数较多时,玩家总是输家。


import random


def gamble(dealer, player, bet, n):
    """接收庄家和玩家资本,每局赌资和赌局数量,返回100局后或有一方赌资输光时的资本数值"""
    for i in range(n):
        r = random.randint(0, 1)
        if r == 1:
            dealer = dealer + bet
            player = player - bet
        else:
            dealer = dealer - bet
            player = player + bet
        if dealer <= 0 or player <= 0:
            return dealer, player, i
    else:
        return dealer, player, n


if __name__ == '__main__':
    dealer, player = 1000000, 10000  # 庄家和玩家的资本
    n = 1000                          # 每个玩家赌1000局
    for bet in [500, 1000, 1500, 2000, 2500, 3000]:  # 每次赌资500
        result = []
        for i in range(1000):  # 共1000个玩家
            result.append(gamble(dealer, player, bet, n))
        print(f'每次押注{bet}元')
        print(f'剩余筹码最大值',max([x[1] for x in result]))
        print(f'剩余筹码最小值',min([x[1] for x in result]))
        print('余额大于等于本金的人数',len([x[1] for x in result if x[1] >= 10000]))  # 余额大于等于本金的人数
        print('余额小于等于0的人数',len([x[1] for x in result if x[1] <= 0]))  # 余额小于等于0的人数
        print()

示例5.14 随机出题的四则运算

In [23]:
import random


def calculator(n, maximum):
    """随机产生n道正整数四则运算的题目,用户输入计算结果,
    判断输入正确与否,并统计正确率。题目保证减法不出现负数."""
    correct = 0
    for i in range(n):                  # 循环n次,每次产生一个新问题
        b = random.randint(0, maximum)  # 随机产生一个maximum以内整数
        a = random.randint(b, maximum)   # 随机产生一个b到maximum以内整数,避免减法出现负数
        sign = random.choice('+-*/')     # 随机获取一个运算符号
        print(f'{a}{sign}{b}=', end='')  # 5+10=,格式化输出
        result = float(input())          # 用户输入计算结果,转浮点数
        if result == eval(f'{a}{sign}{b}'):  # eval()将字符串转表达式并计算
            print('恭喜你,回答正确')
            correct = correct + 1        # 统计回答正确的题目数量
        else:
            print('回答错误,你要加油哦!')
    print(f'答对{correct}题,正确率为{correct/n * 100}%')


if __name__ == '__main__':
    num = int(input('请输入出题数量:'))
    m = int(input('请输入参与计算的最大数字:'))
    calculator(num, m)
    
In [ ]: