Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

random.sample() returning the same random sequence every time?

Tags:

python

random

I'm using python's random.sample(population, k) function to generate a set of random values from a list to create new permutations of that list. The issue is that each time it runs through a loop, it's generating the exact same random sequence. Why is this? I even used random.seed(i) so that the i variable (changing each time through the loop) would seed it a different value each time. Still the same sequence. What gives!@

Here is how I'm using it:

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

which is just being called from the init method of an Individual class. The init method is being called like so...

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

and the init method looks like this...

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

At the end, I'm printing a final word. Here is the output of running this method twice:

Final Word = [[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]]

Final Word = [[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]]

Notice that these two are absolutely identical..

Edit: Since I'm having a hard time picking out code that I think will be useful, yet short enough to go into this post, I've posted a bunch of it on pastebin. http://pastebin.com/f5f068391 This is hopefully a better alternative.. Thanks again

like image 820
Chris Avatar asked Jan 30 '10 17:01

Chris


1 Answers

I'm not sure what you mean by "generating the exact same random sequence". Since you only give us a snippet that can't be run on its own, it's quite possible that there are bugs in other parts of your code that you choose not to show us -- I've tried adding the absolutely minimal amount of code needed to make your snippet run, i.e.:

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

and here's what I see when I run this self-sufficient script a few times:

$ 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']

As you see, that's anything but "the exact same random sequence" -- it's changing each and every run, just as expected.

I imagine I've read your mind incorrectly when trying to make your code executable, and that you mean it to be used very differently than my tiny script uses it -- but then mind-reading is an unreliable art (which is why it would be so much better if you posted a self-contained, runnable example, made as tiny as it can be while still reproducing your problem, rather than forcing us to try and read your mind!-).

Why don't you tweak the stand-alone script I've just posted by the minimal needed amount to make it closer to your intended use, and to reproduce the problem you observe? Then it will be so much easier and more productive for us to spot any issue your code may have and suggest the best way to fix it!

Edit: the OP's code as pasted in pastebin has two bugs that have absolutely nothing to do with random and combine to produce the OP's observed behavior. Here's the relevant part of the code, redacted:

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])

OK, there's another bug here (using old, legacy classes in new code, instead of shiny new style classes which should always be used), but that's not what's biting the OP (yet), so we'll just mention it in passing;-).

Bug 1: since neither __init__ nor any other method ever do an assignment self.chromosome = ..., all the mentions of self.chromosome in the code actually refer to the one and only list Phenotype.chromosome which all instances of the Phenotype class share. So inevitably all such instances will always have exactly the same, identical chromosome, no matter what. Fix: add self.chromosome = [] in __init__ (better also to remove the class-level variables because they do no good whatsoever and only confuse the issue).

Bug 2: look at the following lines of code again to spot it:

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

Got it? self.alleles and the local name alleleSet are all references to exactly the one and the same alleles set (or list, actually) that's passed in -- so the remove call i altering the collection that's passed in. So that collection's left empty after the very first Phenotype is instantiated (which is why despite Bug 1 the chromosome doesn't keep growing: because the alleles collection is left empty forevermore).

Fix: make a copy, e.g. alleleSet = list(self.alleles), to avoid damaging the original collection.

Better fix: what you're doing is an extremely convoluted way to spell much simpler code such as:

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

i.e., just get a random permutation. Building a random permutation by doing N separate samples and removing each sample from the collection as it's generated is a really round-about, slow and complicated way to attack an extremely simple problem!-)

like image 100
Alex Martelli Avatar answered Oct 07 '22 15:10

Alex Martelli