Python中生成随机数据random模块总结

1. 伪随机

查看 python 标准库random的文档, 第一行是 

该模块为各种分布实现了伪随机数生成器

 random模块本质上是用数据的算法来实现的, 生成的数据看似随机, 但依然是可重现的。

1.1 seed, getstate, setstate 

通过指定初始化随机数, random.seed 可以使后续生成的随机数具有确定性.

In [1]: import random

In [2]: random.seed(100)
In [3]: [random.random() for _ in range(3)]
Out[3]: [0.1456692551041303, 0.45492700451402135, 0.7707838056590222]

In [4]: random.seed(100)
In [5]: [random.random() for _ in range(3)]
Out[5]: [0.1456692551041303, 0.45492700451402135, 0.7707838056590222]

 从以上代码可以看到, 在In [4]这行代码将seed重置为100后, 后续再次调用random.random()时, 和第一次产生的数据是完全相同的

除了指定seed, random模块还提供了2个方法 getstate(), setstate() 来捕获和恢复生成器当前内部状态的对象. 

In [6]: st = random.getstate()

In [7]: [random.random() for _ in range(3)]
Out[7]: [0.705513226934028, 0.7319589730332557, 0.43351443489540376]

In [8]: random.setstate(st)

In [9]: [random.random() for _ in range(3)]
Out[9]: [0.705513226934028, 0.7319589730332557, 0.43351443489540376]

2. random中的常用方法

2.1 随机字节 randbytes

randbytes接收1个参数, 用于返回指定长度的随机字节数据

In [10]: random.randbytes(2)
Out[10]: b'\x80d'
In [11]: random.randbytes(4)
Out[11]: b'\xc7np\xfc'

2.2 随机整数  randint, randrange, getrandbits

randint(start, end)接受 2 个参数 返回闭区间 start,  end 中的一个数.

In [1]: import random

In [2]: {random.randint(1, 10) for _ in range(100)}
Out[2]: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

随机取值100次, 将结果转换为一个集合, 可以看到, 数据范围为 [1, 10] 

randrange(start, end, step), 等效于choice(range(start, stop, step)), 从指定起止坐标和步长的列表中随机取一个数据, 数据区间左开右比 start, end )

# randrange 返回右开区间的数据
In [3]: {random.randrange(1, 10) for _ in range(100)}
Out[3]: {1, 2, 3, 4, 5, 6, 7, 8, 9}

# randrange 可以省略start的值, 默认使用0
In [4]: {random.randrange(10) for _ in range(100)}
Out[4]: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

# randrange 指定步长
In [5]: {random.randrange(1, 10, step=3) for _ in range(100)}
Out[5]: {1, 4, 7}

getrandbits(k), 返回一个2进制k位能表达的数据大小中的随机一个数据. 数据范围 [0, 2^k ]

# getrandbits 返回0-2^4之间的数据
In [6]: {random.getrandbits(4) for _ in range(100)}
Out[6]: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

2.3 随机序列 choice, choices, shuffle, sample

choice(seq), 从非空序列seq中随机返回一个值

In [7]: data = ['A', 'B', 'C', 'D', 'E', 'F']

In [8]: random.choice(data)
Out[8]: 'D'
In [9]: random.choice(data)
Out[9]: 'E'

sample(seq, k), 从非空序列seq中随机取k个元素, 返回一个列表, 数据不可以重复.

In [10]: random.sample(data, 2)
Out[10]: ['E', 'B']
In [11]: random.sample(data, 4)
Out[11]: ['D', 'E', 'A', 'F']

shuffle(seq), 将seq数据原地打乱顺序

In [12]: data = ['A', 'B', 'C', 'D', 'E', 'F']
In [13]: random.shuffle(data)
In [14]: data
Out[14]: ['F', 'D', 'C', 'A', 'B', 'E']

choices(seq, weights=None , * , cum_weights=None , k=1), 从非空序列seq中随机取k个元素, 返回一个列表, 数据可以重复, weightscum_weights字段为对应位置数据的权重和相对权重, 如果指定权重, 权重列表需要和seq长度一致.

In [15]: data = ['A', 'B', 'C', 'D', 'E', 'F']
In [16]: random.sample(data, k=8)
---------------------------------------------------------------------------
ValueError: Sample larger than population or is negative

In [17]: random.choices(data, k=8)
Out[17]: ['E', 'A', 'D', 'F', 'F', 'E', 'A', 'C']

从以上代码可以看到, choices与sample的最大不同是, choices获取的数据可以重复, 这样可以生成长度比原数据还要大的列表. 

下面为了便于理解choices函数中的权重参数的使用, 需要使用collections.Counter来更直观的统计随机取的数据的个数.

In [18]: Counter(random.choices(data, k=600))
Out[18]: Counter({'F': 114, 'A': 97, 'C': 103, 'B': 92, 'E': 105, 'D': 89})

可以看到, 默认所有数据权重一致, 所以choices取600个数据, ABCDEF每个数据大概是100次左右.

下面我们指定权重weights字段

In [19]: Counter(random.choices(data, weights=[0, 0, 10, 10, 20, 20], k=600))
Out[19]: Counter({'E': 189, 'F': 209, 'C': 104, 'D': 98})

可以看到, 指定了weithts =[0, 0, 10, 10, 20, 20]之后, 字符AB因为对应的权重为0, 所以结果中1次都没有出现, 字符C D E F 按照配的权重按照 1 : 1 : 2 : 2的比例出现

还可以使用 cum_weights 参数。它代表交换权重。cum_weights与weights的换算关系为 

cum_weights[0] = weights[0]

cum_weights[n] = sum(weights[:n-1])

如  weights = [5,10,20,30,35] 对应的

cum_weights = [5,15,35,65,100]

In [20]: Counter(random.choices(data, cum_weights=[0, 0, 10, 20, 40, 60], k=600))
Out[20]: Counter({'C': 101, 'F': 204, 'E': 198, 'D': 97})

将cum_weights=[0, 0, 10, 20, 40, 60] 可以达到和之前使用weithts =[0, 0, 10, 10, 20, 20], 相同的效果.

?一个常用的使用场景?

生成指定长度的随机字符串

In [1]: import random
In [2]: import string

In [3]: ''.join(random.choices(string.ascii_letters+string.punctuation, k=10))
Out[3]: 'ZmoA,u*{"~'
In [4]: ''.join(random.choices(string.ascii_letters+string.punctuation, k=10))
Out[4]: 'U";}vc\\Fl&'

3. 其他分布方法

如果按照概率分布生成一系列数据, random内置提供了一系列分布函数的随机数方法

函数说明
random()返回范围 [0.0, 1.0) 中的下一个随机浮点数
uniform(a, b)返回[min(a, b), max(a, b)] 范围内的一个随机浮点数
triangular(low, high, mode)具有这些边界之间的指定模式的对称分布

以triangular为例

import random
import matplotlib.pyplot as plt  
  
# store the random numbers in a list
nums = []
low = 10
high = 100
mode = 20
  
for i in range(10000):
    temp = random.triangular(low, high, mode)
    nums.append(temp)
      
# plotting a graph
plt.hist(nums, bins = 200)
plt.show()

输出:

 另外还有 正态分布normalvariate,  高斯分布 gauss等, 本文不再介绍了.

总结:

① Python 的随机是伪随机, 可以通过seed和getstate来获取当前随机的种子状态

② 基本的随机数据方法

  • 字节: randbytes
  • 整数: randint, randrange
  • 序列: choice, sample, choices

③ 内置的概率数据分布模型


版权声明:本文为be5yond原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。