random.sample() 每次返回相同的随机序列?

8 投票
4 回答
36755 浏览
提问于 2025-04-15 18:40

我在用Python的random.sample(population, k)函数从一个列表中随机生成一些值,以此来创建这个列表的新排列。问题是每次在循环中运行时,它生成的随机序列都是完全相同的。为什么会这样呢?我甚至使用了random.seed(i),这样每次循环中i变量的值都会不同,理论上应该能生成不同的随机数,但结果还是一样的。到底怎么回事!

这是我使用的方式:

def initialBuild(self):
    alphabet = self.alphabet
    for i in range (self.length):
        value = random.sample(alphabet, 1)
        alphabet.remove(value[0])
        self.finalWord.append(value[0])
    print "Final word = ", self.finalWord

这个代码是从一个Individual类的初始化方法中调用的。初始化方法是这样被调用的...

def buildPopulation(self, alphabet):
    #Initialize empty individuals
    for i in range(POPULATION_SIZE):
        self.population.append(Individual(alphabet))

而初始化方法看起来是这样的...

def __init__(self, alphabet = []):
    self.length = len(alphabet)
    self.alphabet = alphabet
    self.initialBuild()

最后,我打印了一个最终的结果。这里是运行这个方法两次的输出:

最终结果 = [[1150, 1160], [720, 635], [95, 260], [595, 360], [770, 610], [830, 610], [25, 185], [520, 585], [605, 625], [410, 250], [555, 815], [880, 660], [300, 465], [1220, 580], [1215, 245], [1250, 400], [565, 575], [1605, 620], [845, 680], [1170, 65], [795, 645], [525, 1000], [760, 650], [580, 1175], [420, 555], [25, 230], [345, 750], [700, 500], [725, 370], [1530, 5], [1740, 245], [875, 920], [415, 635], [1340, 725], [975, 580], [575, 665], [1465, 200], [830, 485], [660, 180], [475, 960], [685, 595], [145, 665], [510, 875], [845, 655], [650, 1130], [945, 685], [480, 415], [700, 580], [560, 365], [685, 610], [835, 625], [1320, 315]]

最终结果 = [[1150, 1160], [720, 635], [95, 260], [595, 360], [770, 610], [830, 610], [25, 185], [520, 585], [605, 625], [410, 250], [555, 815], [880, 660], [300, 465], [1220, 580], [1215, 245], [1250, 400], [565, 575], [1605, 620], [845, 680], [1170, 65], [795, 645], [525, 1000], [760, 650], [580, 1175], [420, 555], [25, 230], [345, 750], [700, 500], [725, 370], [1530, 5], [1740, 245], [875, 920], [415, 635], [1340, 725], [975, 580], [575, 665], [1465, 200], [830, 485], [660, 180], [475, 960], [685, 595], [145, 665], [510, 875], [845, 655], [650, 1130], [945, 685], [480, 415], [700, 580], [560, 365], [685, 610], [835, 625], [1320, 315]]

注意这两个结果是完全相同的。

编辑:由于我很难挑选出我认为有用的代码,而且又要足够简短以便放进这个帖子,所以我把一堆代码放在了pastebin上。http://pastebin.com/f5f068391 希望这个更好一些。再次感谢!

4 个回答

1
value = random.sample(alphabet, 1) 

根据我通过反复尝试得到的经验,这段代码会从你的数据中随机选一个值,然后把这个值赋给“value”。之后,“value”就一直是这个第一次随机选出来的值了。它不会再随机选择其他值,而是一直保持最开始赋给“value”的那个值。

而不是:

self.finalWord.append(value[0])

试试这个:

self.finalWord.append(random.sample(alphabet, 1)[0])

我还没有测试过这个,所以不能百分之百确定它会有效。不过,不管怎样,如果我没记错的话,这应该能帮助你理解你的问题。

6

不要为每个样本都改变种子。这会破坏随机数生成器,确保它绝对不是随机的。

种子只需设置一次。在你的应用程序运行期间,永远不要再改变这个值。

随机数生成器是从一个已知的、固定的种子开始的。每次运行时,你应该得到相同的序列。如果不设置种子,就会得到一个可预测的序列。设置任何固定的种子(比如示例中的i)也会得到一个可预测的序列。

如果你想要一个不可预测的序列,就需要一个不可预测的种子。可以使用当前时间,或者从/dev/random读取的一些字节作为种子值。只需设置一次。

可以把这看作是一个简化的说明。

word = random.sample( alphabet, length )

对我来说,它似乎产生了不同的序列。

>>> import string, random
>>> alphabet = string.ascii_lowercase
>>> random.sample( alphabet, 5 )
['x', 'p', 's', 'k', 'h']
>>> random.sample( alphabet, 5 )
['y', 'h', 'u', 'n', 'd']
17

我不太明白你说的“生成完全相同的随机序列”是什么意思。因为你只给了我们一段无法独立运行的代码片段,所以很可能你的其他代码部分有错误,而你选择不展示这些部分。我尝试添加了运行这段代码片段所需的最少代码,像这样:

import random

import string
def self(): pass
self.alphabet = list(string.lowercase)
self.finalWord = []
self.length = 4

for x in range(5):
  alphabet = self.alphabet
  for i in range (self.length):
      value = random.sample(alphabet, 1)
      alphabet.remove(value[0])
      self.finalWord.append(value[0])
  print "Final word = ", self.finalWord

然后这是我运行这个独立脚本几次后看到的结果:

$ python sa.py 
Final word =  ['y', 'm', 'u', 'z']
Final word =  ['y', 'm', 'u', 'z', 'h', 'b', 'c', 's']
Final word =  ['y', 'm', 'u', 'z', 'h', 'b', 'c', 's', 'x', 'l', 'r', 'n']
Final word =  ['y', 'm', 'u', 'z', 'h', 'b', 'c', 's', 'x', 'l', 'r', 'n', 'q', 'a', 'k', 'e']
Final word =  ['y', 'm', 'u', 'z', 'h', 'b', 'c', 's', 'x', 'l', 'r', 'n', 'q', 'a', 'k', 'e', 'p', 'd', 'j', 'w']
$ python sa.py 
Final word =  ['k', 'v', 'o', 'd']
Final word =  ['k', 'v', 'o', 'd', 'q', 'p', 'w', 'l']
Final word =  ['k', 'v', 'o', 'd', 'q', 'p', 'w', 'l', 'n', 'u', 'g', 't']
Final word =  ['k', 'v', 'o', 'd', 'q', 'p', 'w', 'l', 'n', 'u', 'g', 't', 'i', 'r', 'e', 'f']
Final word =  ['k', 'v', 'o', 'd', 'q', 'p', 'w', 'l', 'n', 'u', 'g', 't', 'i', 'r', 'e', 'f', 's', 'c', 'j', 'z']
$ python sa.py 
Final word =  ['o', 'a', 'g', 't']
Final word =  ['o', 'a', 'g', 't', 'k', 'j', 'y', 'w']
Final word =  ['o', 'a', 'g', 't', 'k', 'j', 'y', 'w', 'z', 'l', 'i', 's']
Final word =  ['o', 'a', 'g', 't', 'k', 'j', 'y', 'w', 'z', 'l', 'i', 's', 'u', 'p', 'f', 'm']
Final word =  ['o', 'a', 'g', 't', 'k', 'j', 'y', 'w', 'z', 'l', 'i', 's', 'u', 'p', 'f', 'm', 'h', 'e', 'q', 'v']

如你所见,这根本不是“完全相同的随机序列”——每次运行结果都在变化,这正是我们所期待的。

我想我在尝试让你的代码可执行时,误解了你的意思,你可能是想用它的方式和我这个小脚本完全不同——但心灵感应可不可靠(这就是为什么你最好能提供一个自包含的、可运行的例子,尽量简化到可以重现你遇到的问题,而不是让我们去猜测你的想法!)。

为什么不稍微调整一下我刚刚发布的独立脚本,使其更接近你想要的用法,并重现你观察到的问题呢?这样我们就能更容易、更高效地找出你代码中的问题,并建议最佳的解决方案!

编辑:原贴的代码在pastebin上有两个错误,和random完全无关,但结合在一起导致了原贴观察到的行为。以下是相关代码部分,已做删减:

class Phenotype:
   ...
   chromosome = []

   def __init__(self, alleles = []):
    self.length = len(alleles)
    self.alleles = alleles
    self.initialBuild()

   def initialBuild(self):
    alleleSet = self.alleles
    for i in range (self.length):
        value = random.sample(alleleSet, 1)
        alleleSet.remove(value[0])
        self.chromosome.append(value[0])

好的,这里还有另一个错误(在新代码中使用旧的、过时的类,而不是应该始终使用的新样式类),但这并不是原贴遇到的问题(还没),所以我们只是顺便提一下;-)。

错误1:因为__init__或其他方法从来没有执行过赋值self.chromosome = ...,所以代码中所有提到的self.chromosome实际上都指的是所有Phenotype类实例共享的那个唯一的列表Phenotype.chromosome。所以不可避免地,所有这样的实例将始终拥有完全相同的chromosome,无论如何。修复方法:在__init__中添加self.chromosome = [](最好也去掉类级别的变量,因为它们毫无用处,只会让问题更加混乱)。

错误2:再看看以下几行代码,找出问题:

    self.alleles = alleles
       ...
    alleleSet = self.alleles
       ...
        alleleSet.remove(value[0])

明白了吗?self.alleles和局部变量alleleSet都是对完全相同的传入的alleles集合(实际上是列表)的引用——所以remove调用正在改变传入的集合。因此,在第一个Phenotype实例化后,这个集合就被清空了(这就是为什么尽管有错误1,chromosome却没有不断增长:因为alleles集合被永远清空了)。

修复方法:做一个副本,比如alleleSet = list(self.alleles),以避免损坏原始集合。

更好的修复方法:你现在的做法是一个极其复杂的方式来实现更简单的代码,比如:

self.chromosome = list(self.alleles)
random.shuffle(self.chromosome)

也就是说,直接获取一个随机排列。通过进行N次单独采样并在生成时从集合中移除每个样本来构建随机排列,是一种非常迂回、缓慢且复杂的方式来解决一个极其简单的问题!-)

撰写回答