3 February, 2006
Read on for the code and a few comments.
This code is a bit different than the C++ code I provided previously. Given the flexibility of Python lists, I have the class randomly return an element from the list. You can choose to either pass in your own list, or you can have boolean variables returned similar to the C++ code. (The boolean option just constructs a list of boolean values to select from randomly.)
By default, Python uses the Mersenne Twister method to generate random numbers, so the random numbers generated should be nice and random.
Overall, this code is much more flexible. For example you could more easily do dice rolls. A call to
ro = RandomOdds(range(1,21))
would give the results of rolling a 20-sided die (1d20) without replacement. If you want it to feel a bit more random (while still being fair) you could multiply the range object a few times like such as
ro = RandomOdds(range(1,21)*5)
This code has been created with readability and maintainability as the highest priorities. There are probably some optimizations that could be made for execution efficiency, I’m sure.
Without further ado, here’s the code. Once again, this is released under a BSD-style license which boils down to: use it however you want, just give me credit as appropriate.
# RandomOdds: Returns elements of a list randomly without replacement. # # Copyright (C) 2006, Brian 'Psychochild' Green # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # 3. The names of its contributors may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import random import types class RandomOdds: """ Returns a random value based on what is passed in. Does random results without replacement, in mathematical terms. """ # Initialization def __init__(self, oddsList=None): # Keep track of the full list we want. self.mOddsList =  # Keeps track of value we haven't returned yet. self.mListLeft =  if oddsList == None or not self.SetOdds(oddsList): # Something went wrong, set to default 50/50 boolean return. self.SetOdds([True, False]) # SetBoolOdds - Set boolean odds for the object. This creates a list of # boolean values which we return as normal. # params: successes = the number of successes we want. # outof = maximum number of rolls required to get the number of # successes. # return: True if new odds are set. False otherwise. # Side effect: The status is reset. def SetBoolOdds(self, successes, outof): if successes > outof or successes < 0 or outof < 0: # Probably a mistake, don't change anything. return False accumulator = 1 oddsList =  while accumulator <= outof: if accumulator <= successes: oddsList += [True] else: oddsList += [False] accumulator += 1 return self.SetOdds(oddsList) # SetOdds - Set the odds list for the object. This function takes a list # of values, verifies that it's a list, then shuffles it. Each # subsequent call to GetChance returns one of the list. # params: oddsList = a list of values we want returned. # return: True if new odds are set. False otherwise. # Side effect: The status is reset. def SetOdds(self, oddsList): if (type(oddsList) is not types.ListType) or len(oddsList) <= 0: # Not a populated list, don't change anything. return False self.mOddsList = oddsList self.ResetState() return True # ResetState - Reset the odds back to the original state. def ResetState(self): # Set mListLeft to a copy of mOddsList. self.mListLeft = self.mOddsList[0:] # GetChance - Returns a value, depending on what we set it to be. # Caller is responsible for handling the value properly. def GetChance(self): # Select a value. retIndex = random.randrange(len(self.mListLeft)) retVal = self.mListLeft[retIndex] del self.mListLeft[retIndex] # Do we need to restart? if len(self.mListLeft) <= 0: self.ResetState() return retVal