Python如何生成随机数据的,随机性和安全性是怎样
Admin 2022-11-03 群英技术资讯 313 次浏览
用于在 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 :
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
大多数函数都 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() | 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 |
在不涉及太多细节的情况下,生成依赖于操作系统的随机字节,可以安全地称为密码安全 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)
但是这种保存格式不太符合开发的要求。
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'
生成随机令牌的最后一个选项是 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()是 |
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了基于Python的EasyGUI学习实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
分组统计在很多时候都需要用到,可以实现很多数据库函数的功能。本文主要介绍了python中pandas对多列进行分组统计的实现,感兴趣的可以了解一下
Django提供了翻页器。用Django的Paginator类实现
这篇文章给大家分享的是关于CNN的内容,CNN也就是卷积神经网络,那么CNN到底有什么用呢?可以解决什么问题呢?CNN的权值共享网络结构使之更类似于生物神经网络,降低了网络模型的复杂度,减少了权值的数量。接下来小编就给大家来详细的介绍一下CNN的基础内容,感兴趣的朋友就继续往下看吧。
这篇文章主要为大家详细介绍了python实现21点小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008