基于Python中random.sample()的替代方案

1. Python中的random.sample()

在Python中,如果我们需要从一个序列中随机选择若干个元素,常见的做法是使用random.sample()函数。其用法如下:

import random

sample_list = [1, 2, 3, 4, 5]

random_sample = random.sample(sample_list, 3)

print(random_sample)

运行结果如下:

[3, 5, 1]

以上代码中,random.sample()函数的第一个参数是待抽样的序列,第二个参数是需要抽取的元素个数。该函数的返回值是一个包含抽取的元素的列表。

2. random.sample()的缺陷

然而,random.sample()也存在一些问题。当我们需要从一个巨大的序列中随机选取若干个元素时,调用random.sample()可能会占用大量的内存。这是因为该函数需要将整个序列都加载进内存中,然后再进行统计和随机抽样。

假设我们有一个长度为1亿的序列,需要从其中选取1万个元素,我们可以这样写代码:

import random

big_list = list(range(100000000))

random_sample = random.sample(big_list, 10000)

print(random_sample)

然而,当我们运行该程序时,很可能会遇到MemoryError的错误提示,表示程序无法获取足够的内存。这是因为random.sample()需要将整个长度为1亿的序列全部都加载进内存中,再进行统计和随机抽样,所以内存占用非常大。

3. 替代方案

3.1 使用random.shuffle() + 切片

一种可以替代random.sample()的方法是使用random.shuffle()函数。该函数可以随机打乱一个序列的顺序,我们可以在打乱后取出前k个元素作为随机抽样的结果。

代码如下:

import random

big_list = list(range(100000000))

random.shuffle(big_list)

random_sample = big_list[:10000]

print(random_sample)

运行结果与random.sample()函数相同。

使用random.shuffle()的好处是不需要全部加载整个序列进内存,而是只需要先把序列打乱,然后再取前k个元素,非常节省内存。

然而,这种方法也存在一些问题。当我们需要选取的元素个数非常大时(如上面的例子中要选取100万个元素),该方法就不太实用了。因为使用切片取出前100万个元素仍然会占用大量的内存。而如果使用一个循环一个一个地取,效率又会很低。

3.2 使用生成器

另外一种解决方案是使用生成器,将随机抽样的过程延迟到使用的时候再进行。这样可以避免一次性加载整个序列进内存的问题。

代码如下:

import random

def random_sample(seq, k):

seq_size = len(seq)

for i in range(k):

j = int(random.random() * seq_size)

yield seq[j]

big_list = list(range(100000000))

random_gen = random_sample(big_list, 10000)

random_sample = list(random_gen)

print(random_sample)

运行结果与random.sample()函数相同。

以上代码中,我们定义了一个生成器函数random_sample(),接收一个待抽样的序列和需要抽取的元素个数。该函数在每次迭代时,随机选取一个元素的下标,并返回对应的元素。由于是生成器函数,不会一次性地将整个序列都加载到内存中,而是每次仅在需要的时候生成一个随机抽样的元素,从而避免了内存占用过大的问题。

需要注意的是,在random_sample()函数中,我们使用了random.random()函数生成一个[0, 1)之间的随机数,然后乘以序列的长度,得到一个随机的下标。这种方法比random.randint()函数的效果更好,因为在产生大量的随机数时,random.random()的速度比random.randint()快。

4. 总结

本文介绍了Python中random.sample()函数的使用方法,并提出了该函数存在的一个缺陷:当需要从一个巨大的序列中随机选取若干个元素时,调用random.sample()可能会占用大量的内存。为了解决这个问题,我们介绍了两种可以替代random.sample()的方法:使用random.shuffle() + 切片,以及使用生成器。以上两种方法都可以避免一次性加载整个序列进内存的问题。

后端开发标签