Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Lib/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,7 @@ def gammavariate(self, alpha, beta):

elif alpha == 1.0:
# expovariate(1/beta)
u = random()
while u <= 1e-7:
u = random()
return -_log(u) * beta
return -_log(1.0 - random()) * beta

else: # alpha is between 0 and 1 (exclusive)

Expand Down
42 changes: 31 additions & 11 deletions Lib/test/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,28 +791,48 @@ def test_gammavariate_errors(self):
self.assertRaises(ValueError, random.gammavariate, 2, 0)
self.assertRaises(ValueError, random.gammavariate, 1, -3)

# There are three different possibilities in the current implementation
# of random.gammavariate(), depending on the value of 'alpha'. What we
# are going to do here is to fix the values returned by random() to
# generate test cases that provide 100% line coverage of the method.
@unittest.mock.patch('random.Random.random')
def test_gammavariate_full_code_coverage(self, random_mock):
# There are three different possibilities in the current implementation
# of random.gammavariate(), depending on the value of 'alpha'. What we
# are going to do here is to fix the values returned by random() to
# generate test cases that provide 100% line coverage of the method.
def test_gammavariate_alpha_greater_one(self, random_mock):

# #1: alpha > 1.0: we want the first random number to be outside the
# #1: alpha > 1.0.
# We want the first random number to be outside the
# [1e-7, .9999999] range, so that the continue statement executes
# once. The values of u1 and u2 will be 0.5 and 0.3, respectively.
random_mock.side_effect = [1e-8, 0.5, 0.3]
returned_value = random.gammavariate(1.1, 2.3)
self.assertAlmostEqual(returned_value, 2.53)

# #2: alpha == 1: first random number less than 1e-7 to that the body
# of the while loop executes once. Then random.random() returns 0.45,
@unittest.mock.patch('random.Random.random')
def test_gammavariate_alpha_equal_one(self, random_mock):

# #2.a: alpha == 1.
# The execution body of the while loop executes once.
# Then random.random() returns 0.45,
# which causes while to stop looping and the algorithm to terminate.
random_mock.side_effect = [1e-8, 0.45]
random_mock.side_effect = [0.45]
returned_value = random.gammavariate(1.0, 3.14)
self.assertAlmostEqual(returned_value, 2.507314166123803)
self.assertAlmostEqual(returned_value, 1.877208182372648)

@unittest.mock.patch('random.Random.random')
def test_gammavariate_alpha_equal_one_equals_expovariate(self, random_mock):

# #2.b: alpha == 1.
# It must be equivalent of calling expovariate(1.0 / beta).
beta = 3.14
random_mock.side_effect = [1e-8, 1e-8]
gammavariate_returned_value = random.gammavariate(1.0, beta)
expovariate_returned_value = random.expovariate(1.0 / beta)
self.assertAlmostEqual(gammavariate_returned_value, expovariate_returned_value)

@unittest.mock.patch('random.Random.random')
def test_gammavariate_alpha_between_zero_and_one(self, random_mock):

# #3: 0 < alpha < 1. This is the most complex region of code to cover,
# #3: 0 < alpha < 1.
# This is the most complex region of code to cover,
# as there are multiple if-else statements. Let's take a look at the
# source code, and determine the values that we need accordingly:
#
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
random.gammavariate(1.0, beta) now computes the same result as
random.expovariate(1.0 / beta). This synchonizes the two algorithms and
eliminates some idiosyncrasies in the old implementation. It does however
produce a difference stream of random variables than it used to.