views:

260

answers:

2

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

+3  A: 

Do not change the seed for every sample. That breaks the random number generator and absolutely assures that it's not random.

Set the seed exactly once. And never change that value again while your application is running.

The Random Number generator starts with a known, constant seed. Each time you run, you should get the same sequence. Setting no seed gives a predictable sequence. Setting any constant seed (like i in the example) gives a predictable sequence.

If you want an unpredictable sequence, you need an unpredictable seed. Use the time-of-day, or some bytes read from /dev/random as seed values. Once.

Consider this as a simplification.

word = random.sample( alphabet, length )

It seems to produce different sequences for me.

>>> 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']
S.Lott
The seed was added after the random sampling wasn't changing. This doesn't really solve anything. The issue isn't that it's identical each time the program runs. It's identical each time the *loop* runs. It gives me the exact same output a thousand times in one run of the application.
Chris
It's producing different sequences for me, but when I loop around and recreate a list I get the identical one. Hold on, I will edit with my output.
Chris
+4  A: 

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

Alex Martelli
I shall also add more code :) I've added the output of running this method twice, but hold on and I'll edit more, thanks!
Chris
Yep, your problems are completely different, let me edit the answer to explain.
Alex Martelli
Wow, unreal. Thanks for the detail. Using classes in python for the first time and it has me a little tied up. Thanks for the effort, and for telling me about random.shuffle!
Chris