From 63d152232e1742660f481c04a811f824b91f6790 Mon Sep 17 00:00:00 2001 From: leodema Date: Mon, 24 Dec 2018 07:54:25 +0100 Subject: [PATCH] bpo-30561: Sync-up expovariate() and gammavariate code (GH-1934) --- Lib/random.py | 5 +- Lib/test/test_random.py | 46 +++++++++++++------ .../2018-12-23-22-27-59.bpo-30561.PSRQ2w.rst | 4 ++ 3 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-12-23-22-27-59.bpo-30561.PSRQ2w.rst diff --git a/Lib/random.py b/Lib/random.py index 9c2904cfd2f..e00a0262389 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -582,10 +582,7 @@ class Random(_random.Random): 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) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 38fd8a9105e..b35cb39e751 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -853,28 +853,48 @@ class TestDistributions(unittest.TestCase): 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, - # which causes while to stop looping and the algorithm to terminate. - random_mock.side_effect = [1e-8, 0.45] - returned_value = random.gammavariate(1.0, 3.14) - self.assertAlmostEqual(returned_value, 2.507314166123803) + @unittest.mock.patch('random.Random.random') + def test_gammavariate_alpha_equal_one(self, random_mock): - # #3: 0 < alpha < 1. This is the most complex region of code to cover, + # #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 = [0.45] + returned_value = random.gammavariate(1.0, 3.14) + 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, # as there are multiple if-else statements. Let's take a look at the # source code, and determine the values that we need accordingly: # diff --git a/Misc/NEWS.d/next/Library/2018-12-23-22-27-59.bpo-30561.PSRQ2w.rst b/Misc/NEWS.d/next/Library/2018-12-23-22-27-59.bpo-30561.PSRQ2w.rst new file mode 100644 index 00000000000..ae99b7cb0ae --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-23-22-27-59.bpo-30561.PSRQ2w.rst @@ -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.