您的位置:首页 > 编程语言 > Python开发

random——模块

2018-02-14 18:02 211 查看

random——产生伪随机数

模块用途

random 模块是为各种分布实现伪随机数发生器。

几乎模块中所有函数都依赖于基本函数random(),它在半开放范围[0.0,1.0)内产生一个统一的随机浮点数。Python使用Mersenne Twister作为核心生成器。它生成53位精度浮点数,周期为2**19937-1.C中的底层实现即快速又线程安全。

注:Mersenne Twister是存在的最广泛测试的随机数发生器之一。但是,这是完全确定性的,并不适用于所有目的,并且完全不适合加密的目的


警告:该模块的伪随机生成器不应用于安全目的。为了安全或加密使用

_randbelow()的部分源码

BPF = 53
# 返回一个小于n的随机数(计算随机数的实例)
def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,
Method=_MethodType, BuiltinMethod=_BuiltinMethodType):

"Return a random int in the range [0,n).  Raises ValueError if n==0."

random = self.random            # 将父类的random方法赋值给random
getrandbits = self.getrandbits  # 将父类的getrandbits方法赋值给getrandbits

# 如果原始的random()内置方法没有被覆盖 或者 提供了一个新的getrandbits() 才可以调用self.getrandbits
if type(random) is BuiltinMethod or type(getrandbits) is Method:
# 如果 random 是内置函数 或 getrandbits 是 方法

# k 是 开始位置转换成二进制的长度
k = n.bit_length()  # don't use (n-1) here because n can be 1
# 计算 一个随机数
r = getrandbits(k)          # 0 <= r < 2**k
while r >= n:
r = getrandbits(k)
# 返回一个小于n的随机数
return r

# 如果复写了random()方法但是没有复写getrandbits()方法 我们只能用自己的random()方法
if n >= maxsize:
# 如果n >= maxsize (maxsize = 9007199254740992) 就调用random()方法
_warn("Underlying random() generator does not supply \n"
"enough bits to choose from a population range this large.\n"
"To remove the range limitation, add a getrandbits() method.")
return int(random() * n) # random() 方法 是 随机[0,1) 的一个数
rem = maxsize % n
limit = (maxsize - rem) / maxsize   # int(limit * maxsize) % n == 0
r = random()    # r < liimit
while r >= limit:
r = random()
return int(r*maxsize) % n


随机整数

random.randrange(self, start, stop=None, step=1, _int=int)

start: 起始数值

stop=None:结束数值 默认为 None

step=1步长 默认为1

random.randrange(10)

​ ——产生一个[0,10)的随机整数

import random
print(random.randrange(10))

5


以下为randrange部分源码

def randrange(self, start, stop=None, step=1, _int=int):
istart = _int(start)
if istart != start:
raise ValueError("non-integer arg 1 for randrange()")
# 如果stop是空 即 我们只传了一个参数即 random.randrange(10)
if stop is None:
if istart > 0:
# 返回一个小于 start 的 随机数
return self._randbelow(istart)
raise ValueError("empty range for randrange()")


random.randrange(10)

如果我们传入的参数是一个大于0的数字,就会通过_randbelow(10)进行运算得到返回值

random.randrange(5,20)

​ ——产生一个[5,20)的随机整数

import random
print(random.randrange(5, 20))

7


以下是randrange部分源码

def randrange(self, start, stop=None, step=1, _int=int):
# stop argument supplied.
istop = _int(stop)
if istop != stop:
raise ValueError("non-integer stop for randrange()")
width = istop - istart # 范围 是一个整数
if step == 1 and width > 0: # 如果步长等于1 且 范围大于零
# 返回 一个 start + 小于范围的随机数
return istart + self._randbelow(width) # _randbelow()方法见上面
if step == 1:
# 如果范围 <= 0 则抛出以下异常 具体示例 见 Lrandrange_E1
raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, widt
4000
h))


random.randrange(5,20)

如果我们传入的参数为x, y 且(y - x )> 0 就会通过x + _randbelow(y - x) 进行运算得到返回值

Lrandrange_E1

import random
print(random.randrange(1,1,1))

Traceback (most recent call last):
File "D:/random_demo.py", line 20, in <module>
print(random.randrange(1,1,1))
File "D:/random_demo.py", line 198, in randrange
raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
ValueError: empty range for randrange() (1,1, 0)


random.randrange(5,-1,-2)

​ ——产生(5,-1,-2)的随机数

import random
print(random.randrange(5,-1,-2))

3


random.randrange(5,20,5)

​ ——产生(5,20,5)的随机数

import random
print(random.randrange(5, 20, 5))

10


以下是randrange部分源码

def randrange(self, start, stop=None, step=1, _int=int):
istep = _int(step)
if istep != step:
raise ValueError("non-integer step for randrange()")

# 要么 start < stop and istep > 0
# 要么 start > stop and istep < 0
# 其余情况报错
if istep > 0:
# width = istop - istart
n = (width + istep - 1) // istep
elif istep < 0:
n = (width + istep + 1) // istep
else:
raise ValueError("zero step for randrange()")

if n <= 0:
raise ValueError("empty range for randrange()")
# 返回 start + 步长 * 小于n的随机整数
return istart + istep*self._randbelow(n)


如果我们传入的参数为为x, y, z

( z < 0 )且(y - x )< 0 就会通过x + z * _randbelow((y - x + 1) // z) 进行运算得到返回值

( z > 0 )且(y - x )> 0 就会通过x + z * _randbelow((y - x - 1) // z) 进行运算得到返回值

((y - x + 1) // z) 是计算了 可取的范围(其实就是计算了[x, y)这个范围中有最多可取到多少个z)

根据这个数取一个小于它的随机数然后乘步长 + x

random.randint(a,b)

返回一个随机整数N使得。别名 。
a <= N <= b``randrange(a, b+1)


def randint(self, a, b):
"""Return random integer in range [a, b], including both end points.
"""

return self.randrange(a, b+1)


so~ randint() 其实就是调用了 randrange()方法 也就是一个简化版的randrange()

序列函数

random.choice(self, seq)

​ ——在一个非空的序列中选择一个元素

import random
print(random.choice([1,2,3]))

1


源码如下

def choice(self, seq):
try:
# 选择一个随机索引
i = self._randbelow(len(seq))
except ValueError:
# 如果序列为空 则抛出异常
raise IndexError('Cannot choose from an empty sequence')
# 通过索引找到随机的元素
return seq[i]


random.choice([1,2,3])

在这个list列表中 根据列表的长度随机算出一个小于列表长度的数作为索引,返回该索引的值

random.shuffle(self, x, random=None)

将序列x随机混合(将x随机打乱)

import random
l = [12,11,10]
random.shuffle(l)
print(l)

[12, 10, 11]


源码如下

def shuffle(self, x, random=None):
''' x 列表(可迭代对象)
random 是一个 计算随机数的方法
'''
# random 为空 即 我们没有手动传入一个计算随机数的方法
if random is None
# 赋予self._randbelow方法 一个别名 randbelow
randbelow = self._randbelow

# 循环 len 次每次将索引为i的 值 和 索引为 一个小于i的随机整数的值进行替换
for i in reversed(range(1, len(x))):
# 通过self._randbelow方法进行计算随机数得到j的值
j = randbelow(i+1)
# 交换 索引为i 和索引为j的值
x[i], x[j] = x[j], x[i]

# 如果我们手动传入了一个随机数方法
else:
_int = int
# 循环 len 次每次将索引为i的 值 和 索引为 一个小于i的随机整数 的值进行替换
for i in reversed(range(1, len(x))):
# 通过我们手动传入的随机数方法的结果 * (i+1) 得到j的值
j = _int(random() * (i+1))
# 交换 索引为i 和索引为j的值
x[i], x[j] = x[j], x[i]


shuffle方法其实是不断进行两两索引位置的值进行交换 达到打乱顺序的

并且它是在原本的列表上进行打乱的,而没有新的列表生成

random.sample(self, population, k)

​ ——返回从总体序列或集合中选择的唯一元素的k长度列表。用于无需更换的随机抽样。

import random
l = [1,2,3,4,5]
l2 = random.sample(l,2)
print(l2)

[4, 3]


源码如下

def sample(self, population, k):
# 判断population是否是可使用sample的类型(如果是set转成tuple 如果是tuple、list就继续运行 如果是dict直接报错)
if isinstance(population, _Set):
population = tuple(population)
if not isinstance(population, _Sequence):
raise TypeError("Population must be a sequence or set.  For dicts, use list(d).")
# self._randbelow 命名一个别名randbelow
randbelow = self._randbelow
# 计算population的长度
n = len(population)
# 如果k >=n 或者<=0 就报错, k是选择元素的长度
if not 0 <= k <= n:
raise ValueError("Sample larger than population or is negative")
# 设置默认值
result = [None] * k
setsize = 21        # 默认为小集合
# 如果k大于5则认为 是一个大集合 否则是小集合
if k > 5:
setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets

# 如果是小集合(An n-length list is smaller than a k-length set)
if n <= setsize:
# 将population强转成list
pool = list(population)
# 这里!!! 其实就是为了每次都可以选出列表中的一个数(选出一个之前没有选择过的数) 然后依次赋给result 这个之前设置的默认值为None 的列表
for i in range(k):         # invariant:  non-selected at [0,n-i)
# 随机一个 n-i 的整数当作 下标赋给j
j = randbelow(n-i)
# 这下面两句就是在 把选中的数替换成倒置位的数 (因为倒置位的数一直无法取到所以将其替换到前面 )
result[i] = pool[j]     # 将值赋给  result列表
pool[j] = pool[n-i-1]   # move non-selected item into vacancy

# 猜测是因为大集合如果转换成list太过于占空间 所以单独 拿了出来
else:
# 创建了一个set 对象
selected = set()
selected_add = selected.add # 给set对象的add方法 赋值一个别名

for i in range(k):
j = randbelow(n) # 一个小于n的随机数 并赋给j 当作下标

# 如果j 不在selected中,即 我们没有选择过 下标为j的数,就跳过while循环
# 如果在selected中,即 我们曾经选择到 下标为j的数, 则重新取随机数 直到,选择一个从来没有使用过的j
while j in selected:
j = randbelow(n)

selected_add(j)     # 将j添加到集合中
result[i] = population[j] # 将值赋给  result列表
return result


簿记功能

random.seed(self, a=None, version=2)

​ ——seed() 方法改变随机数生成器的种子,可以在调用其他随机模块函数之前调用此函数

改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。

import random
random.seed(0)
print(random.random())
random.seed(0)
print(random.random())


0.8445167133186481
0.8445167133186481


源码如下

def seed(self, a=None, version=2):
# a -- 改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。
# 默认调用第二个方式 version = 2

# 是方案1 并且 a是str 或 bytes
if version == 1 and isinstance(a, (str, bytes)):
# 如果 a
bed0
不为None 则 进行运算 否则x 为0
x = ord(a[0]) << 7 if a else 0
for c in a:
x = ((1000003 * x) ^ ord(c)) & 0xFFFFFFFFFFFFFFFF
x ^= len(a)
a = -2 if x == -1 else x
# 是方案2 并且 a是str 或 bytes 或 bytearray
if version == 2 and isinstance(a, (str, bytes, bytearray)):
# 如果a是字符串则 转成bytes类型
if isinstance(a, str):
a = a.encode()
# a 进行加密并进行拼接
a += _sha512(a).digest()
a = int.from_bytes(a, 'big')

# 调用父类的seed(父类中根据当前时间进行生成)
super().seed(a)
self.gauss_next = None


seed( ) 用于指定随机数生成时所用算法开始的整数值,如果使用相同的seed( )值,则每次生成的随即数都相同,如果不设置这个值,则系统根据时间来自己选择这个值,此时每次生成的随机数因时间差异而不同。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  random python 进阶