Python中生成随机数据安全性、多功能性、用途和速度方面进行比较


Posted in Python onApril 14, 2022

在日常工作编程中存在着各种随机事件,同样在编程中生成随机数字的时候也是一样,随机有多随机呢?在涉及信息安全的情况下,它是最重要的问题之一。每当在 Python 中生成随机数据、字符串或数字时,最好至少大致了解这些数据是如何生成的。

用于在 Python 中生成随机数据的不同选项,然后在安全性、多功能性、用途和速度方面对每个选项进行比较。

本篇内容不是数学或密码学相关内容,仅仅是根据需要进行尽可能多的数学运算仅此而已。

随机性有多随机

大多数用 Python 生成的随机数据在科学意义上并不是完全随机的。相反是伪随机的:使用伪随机数生成器(PRNG)生成,它本质上是任何用于生成看似随机但仍可重现的数据的算法。『真』随机数可以由真随机数生成器(TRNG)生成。

可能已经 Python 中看到过类似 random.seed(999) 的东西。此函数调用 Python 模块 random.seed(1234) 使用的底层随机数生成器。random 使得后续调用生成随机数具有确定性:输入 A 总是产生输出 B。

也许『随机』和『确定性』这两个术语似乎不能并存。为了更清楚地说明这一点这里有一个极其精简的版本,random() 它通过使用迭代创建一个『随机』数字 x = (x * 3) % 19 。x 最初定义为种子值,然后根据该种子变形为确定性的数字序列。

class NotSoRandom(object):
    def seed(self, a=3):
        """随机数生成器"""
        self.seedval = a
    def random(self):
        """随机数"""
        self.seedval = (self.seedval * 3) % 19
        return self.seedval

_inst = NotSoRandom()
seed = _inst.seed
random = _inst.random

for i in range(10):
    seed(123)
    print([random() for _ in range(10)])
    
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]
[8, 5, 15, 7, 2, 6, 18, 16, 10, 11]

加密安全性

如果对『RNG』首字母缩略词还不够了解,再添加一个 CSPRNG,或加密安全 PRNG。 CSPRNG 适用于生成敏感数据,例如密码、身份验证器和令牌。 给定一个随机字符串,实际上无法确定在随机字符串序列中哪个字符串出现在该字符串之前或之后。

另一个术语熵,引入或期望的随机性数量。 例如将在介绍的一个 Python 模块定义了 DEFAULT_ENTROPY = 32,即默认返回的字节数。

关于 CSPRNG 的一个关键点是它们仍然是伪随机的。它们以某种内部确定性的方式设计,但添加了一些其他变量或具有使它们『足够随机』以禁止返回到任何强制执行确定性的函数的属性。

Python 工具中的 PRNG 和 CSPRNG :

  • PRNG 选项包括 Python 标准库中的 random 模块及其基于数组的 NumPy 对应模块 numpy.random。
  • Python 的 os、secrets 和 uuid 模块包含用于生成加密安全对象的函数。

PRNG

Python中生成随机数据安全性、多功能性、用途和速度方面进行比较

random 模块

random模块是在 Python 中生成随机数据的最广为人知的工具可,使用Mersenne Twister PRNG 算法作为其核心生成器。

构建一些没有播种的随机数据。该 random.random() 函数返回区间 [0.0, 1.0) 内的随机浮点数。

import random
random.random()
0.1250920165739744
random.random()
0.7327868824782764

使用 random.seed(),可以使结果可重现,并且之后的调用链random.seed() 将产生相同的数据轨迹。

随机数序列变为确定性的,或完全由种子值确定。

random.seed(444)
random.random()
0.3088946587429545
random.random()
0.01323751590501987

random.seed(444)
random.random()
0.3088946587429545
random.random()
0.01323751590501987

使用 random.randint() 可以使用该函数在 Python 中的两个端点之间生成一个随机整数。数据在整个 [x, y] 区间并且可能包括两个端点。

>>> random.randint(0, 10)
2
>>> random.randint(500, 50000)
9991

使用 random.randrange() 可以排除区间的右侧,生成的数字始终位于 [x, y) 范围内,并且始终小于右端点。

random.randrange(1, 10)
9

使用 random.uniform(),从连续均匀分布中提取生成位于特定 [x, y] 区间内的随机浮点数。

random.uniform(20, 30)
27.42639687016509
random.uniform(30, 40)
36.33865802745107

使用 random.choice() 从非空序列(如列表或元组)中选择随机元素。

items = ['A', 'B', 'C', 'D', 'E']
random.choice(items)
'B'

random.choices(items, k=2)
['A', 'C']
random.choices(items, k=3)
['C', 'D', 'E']

使用 random.sample() 不替换的情况下模拟采样。

random.sample(items, 4)
['A', 'D', 'B', 'E']

使用 random.shuffle() 修改序列对象并随机化元素的顺序。

random.shuffle(items)
items
['E', 'B', 'A', 'C', 'D']

生成一系列唯一长度一致的随机字符串的例子,一般用于验证码这种。

from random import Random

# 随机生成邮件验证码的随机字符串
def RandomsStr(random_length):
    Str = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'  # 设置可选字符
    length = len(chars) - 1
    random = Random()
    for i in range(random_length):
        Str += chars[random.randint(0, length)]
    return Str

RandomsStr(10)
LhK3vFepch

RandomsStr(16)
iGy1g0FO54Cjx3WP

数组 numpy.random

大多数函数都 random 返回一个标量值(单个int、float或其他对象)。生成序列的话可以使用列表生成的方法。

[random.random() for _ in range(5)]
[0.7401011155476498,
 0.9892634439644596,
 0.36991622177966765, 
 0.14950913503744223, 
 0.4868906039708182]

numpy.random 使用自己的 PRNG,与普通的 random 不太一样。

"""
从标准正态分布返回样本
"""
np.random.randn(5)
array([-0.59656657, -0.6271152 , -1.51244475, -1.02445644, -0.36722254])

np.random.randn(3, 4)
array([[ 0.34054183,  1.59173609, -0.5257795 , -0.86912511],
       [-0.86855499, -0.64487065,  1.47682128,  1.8238103 ],
       [ 0.05477224,  0.35452769,  0.14088743,  0.55049185]])

"""
根据概率随机分配
"""
np.random.choice([0, 1], p=[0.6, 0.4], size=(5, 4))
array([[0, 1, 0, 1],
       [0, 1, 1, 1],
       [0, 1, 0, 1],
       [1, 0, 0, 0],
       [0, 0, 0, 0]])

"""
创建一系列随机布尔值
"""
np.random.randint(0, 2, size=25, dtype=np.uint8).view(bool)
array([ True, False,  True,  True, False,  True, False, False, False,
       False, False,  True,  True, False, False, False,  True, False,
        True, False,  True,  True,  True, False,  True])

相关数据的生成

假设要模拟两个相关的时间序列。解决此问题的一种方法是使用 NumPy 的multivariate_normal() 函数,该函数将协方差矩阵考虑在内。换句话说要从单个正态分布的随机变量中提取,需要指定其均值和方差(或标准差)。

def corr2cov(p, s):
    """相关性和标准差的协方差矩阵"""
    d = np.diag(s)
    return d @ p @ d
corr = np.array([[1., -0.40],[-0.40, 1.]])
stdev = np.array([6., 1.])
mean = np.array([2., 0.5])
cov = corr2cov(corr, stdev)
data = np.random.multivariate_normal(mean=mean, cov=cov, size=50)
data[:10]

[[-0.33377432  0.22889428]
 [-1.5311996   0.31678635]
 [-6.02684472  0.90562824]
 [ 5.2696086   0.86518295]
 [ 6.43832395  0.36507745]
 [-8.49347011  0.68663565]
 [-5.05968126  0.55214914]
 [ 2.02314646  1.32325775]
 [ 0.98705556 -0.63118682]
 [ 2.90724439 -1.26188307]]

random模块与NumPy对照表

random模块 NumPy 对应方 说明
random() rand() [0.0, 1.0) 中的随机浮点数
randint(a, b) random_integers() [a, b] 中的随机整数
randrange(a, b[, step]) randint() [a, b) 中的随机整数
uniform(a, b) uniform() [a, b] 中的随机浮点数
choice(seq) choice() 随机元素来自seq
choices(seq, k=1) choice() 带有替换的随机k元素seq
sample(population, k) choice()和replace=False 无替换的随机k元素seq
shuffle(x[, random]) shuffle() 将序列随机打乱
normalvariate(mu, sigma)或者gauss(mu, sigma) normal() mu具有均值和标准差的正态分布样本sigma

CSPRNG

Python中生成随机数据安全性、多功能性、用途和速度方面进行比较

尽可能随机 os.urandom()

在不涉及太多细节的情况下,生成依赖于操作系统的随机字节,可以安全地称为密码安全 secretsuuidos.urandom(),在技术上仍然是伪随机的。

唯一的参数是要返回的字节数。

os.urandom(3)
b'\xa2\xe8\x02'

x = os.urandom(6)
x
b'\xce\x11\xe7"!\x84'

type(x), len(x)
(bytes, 6)

但是这种保存格式不太符合开发的要求。

secrets 最佳保存方式

Python 3.6+ 版本引入的 PEP,secrets模块旨在成为事实上的 Python 模块,用于生成加密安全的随机字节和字符串。

secrets 基本上是一个包装器 os.urandom()。只导出了少数用于生成随机数、字节和字符串的函数。

n = 16

# 生成安全令牌
secrets.token_bytes(n)
b'A\x8cz\xe1o\xf9!;\x8b\xf2\x80pJ\x8b\xd4\xd3'
secrets.token_hex(n)
'9cb190491e01230ec4239cae643f286f'  
secrets.token_urlsafe(n)
'MJoi7CknFu3YN41m88SEgQ'
# `random.choice()` 的安全版本
secrets.choice('rain')
'a'

UUID

生成随机令牌的最后一个选项是 Python 的 uuid 模块中的 uuid4() 函数。 UUID 是一个通用唯一标识符,一个 128 位序列(长度为 32 的字符串),旨在『保证跨空间和时间的唯一性』。 uuid4() 是该模块最有用的函数之一,该函数也使用了 os.urandom()。

import uuid

uuid.uuid4()
UUID('3e3ef28d-3ff0-4933-9bba-e5ee91ce0e7b')
uuid.uuid4()
UUID('2e115fcb-5761-4fa1-8287-19f4ee2877ac')

可能还看到了其他一些变体:uuid1()、uuid3() 和 uuid5()。它们之间的主要区别在于这 uuid4() 三个函数都采用某种形式的输入,不符合 uuid4() 的『保证跨空间和时间的唯一性』。

除了安全模块(例如 secrets)之外,Python 的 random 模块实际上还有一个很少使用的类,称为 SystemRandom,它使用 os.urandom()。 (反过来,SystemRandom 也被秘密使用。这有点像一个可以追溯到 urandom() 的网络。)

那么为什么不『默认』这个版本? 为什么不『永远安全』,而不是默认使用在密码学上不安全的确定性随机函数?

1.因为有时希望数据具有确定性和可重复性,以供其他人后续使用。

2.时间效率问题。

"""
CSPRNG 至少在 Python 中,往往比 PRNG 慢得多。 
让我们使用脚本 timed.py 来测试,该脚本使用 timeit.repeat() 比较 randint() 的 PRNG 和 CSPRNG 版本。
"""

import random
import timeit

# CSPRNG 版本依次使用 `SystemRandom()` 和 `os.urandom()`。
_sysrand = random.SystemRandom()

def prng() -> None:
    random.randint(0, 95)

def csprng() -> None:
    _sysrand.randint(0, 95)

setup = 'import random; from __main__ import prng, csprng'

if __name__ == '__main__':
    print('Best of 3 trials with 1,000,000 loops per trial:')

    for f in ('prng()', 'csprng()'):
        best = min(timeit.repeat(f, setup=setup))
        print('\t{:8s} {:0.2f} seconds total time.'.format(f, best))

Best of 3 trials with 1,000,000 loops per trial:
	prng()   0.93 seconds total time.
	csprng() 1.70 seconds total time.

工程随机性的比较

封装/模块 描述 加密安全
random 使用 Mersenne Twister 快速简单的随机数据
numpy.random 像random但对于(可能是多维的)数组
os 包含urandom(),这里介绍的其他功能的基础
secrets 设计为 Python 的事实上的模块,用于生成安全的随机数、字节和字符串
uuid 用于构建 128 位标识符的一些函数的所在地 uuid4()是

到此这篇关于详解Python中生成随机数据的示例详解的文章就介绍到这了!

Python 相关文章推荐
Python全局变量操作详解
Apr 14 Python
安装Python的教程-Windows
Jul 22 Python
在python中使用正则表达式查找可嵌套字符串组
Oct 24 Python
python使用pil进行图像处理(等比例压缩、裁剪)实例代码
Dec 11 Python
Win7下Python与Tensorflow-CPU版开发环境的安装与配置过程
Jan 04 Python
python与sqlite3实现解密chrome cookie实例代码
Jan 20 Python
感知器基础原理及python实现过程详解
Sep 30 Python
python bluetooth蓝牙信息获取蓝牙设备类型的方法
Nov 29 Python
np.newaxis 实现为 numpy.ndarray(多维数组)增加一个轴
Nov 30 Python
详解matplotlib绘图样式(style)初探
Feb 03 Python
Python手拉手教你爬取贝壳房源数据的实战教程
May 21 Python
Python图片验证码降噪和8邻域降噪
Aug 30 Python
python中 .npy文件的读写操作实例
Apr 14 #Python
Python Matplotlib绘制等高线图与渐变色扇形图
python读取并查看npz/npy文件数据以及数据显示方法
Apr 14 #Python
在NumPy中深拷贝和浅拷贝相关操作的定义和背后的原理
Python捕获、播放和保存摄像头视频并提高视频清晰度和对比度
Apr 14 #Python
Python中time标准库的使用教程
Apr 13 #Python
Python函数对象与闭包函数
Apr 13 #Python
You might like
十天学会php之第二天
2006/10/09 PHP
一步一步学习PHP(8) php 数组
2010/03/05 PHP
PHP产生不重复随机数的5个方法总结
2014/11/12 PHP
PHP中使用OpenSSL生成证书及加密解密
2017/02/05 PHP
PHP实现支持CURL字符串证书传输的方法
2019/03/23 PHP
AJAX架构之Dojo篇
2007/04/10 Javascript
JavaScript语法着色引擎(demo及打包文件下载)
2007/06/13 Javascript
JavaScript 开发工具webstrom使用指南
2014/12/09 Javascript
javascript控制层显示或隐藏的方法
2015/07/22 Javascript
jquery简单实现幻灯片的方法
2015/08/03 Javascript
Bootstrap + AngularJS 实现简单的数据过滤字符查找功能
2017/07/27 Javascript
基于js 本地存储(详解)
2017/08/16 Javascript
为什么我们要做三份 Webpack 配置文件
2017/09/18 Javascript
详解webpack打包后如何调试的方法步骤
2018/11/07 Javascript
使用pm2自动化部署node项目的方法步骤
2019/01/28 Javascript
vue+element UI实现树形表格带复选框的示例代码
2019/04/16 Javascript
vue控制多行文字展开收起的实现示例
2019/10/11 Javascript
es6函数之尾递归用法实例分析
2020/04/25 Javascript
vue 项目引入echarts 添加点击事件操作
2020/09/09 Javascript
[01:07:17]EG vs Optic Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
[51:36]EG vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
Phantomjs抓取渲染JS后的网页(Python代码)
2016/05/13 Python
python生成随机图形验证码详解
2017/11/08 Python
Flask模拟实现CSRF攻击的方法
2018/07/24 Python
Python两个字典键同值相加的几种方法
2019/03/05 Python
python获取依赖包和安装依赖包教程
2020/02/13 Python
Python count函数使用方法实例解析
2020/03/23 Python
网页布局中CSS样式无效的十个重要原因详解
2017/08/10 HTML / CSS
实现CSS3中的border-radius(边框圆角)示例代码
2013/07/19 HTML / CSS
eBay爱尔兰站:eBay.ie
2019/08/09 全球购物
大唐面试试题(CPU,UNIX等等)
2012/01/11 面试题
培训师岗位职责
2015/02/14 职场文书
创业计划书之o2o水果店
2019/08/30 职场文书
Spring Boot两种全局配置和两种注解的操作方法
2021/06/29 Java/Android
vue自定义右键菜单之全局实现
2022/04/09 Vue.js
el-form每行显示两列底部按钮居中效果的实现
2022/08/05 HTML / CSS