[Python-checkins] bpo-30561: Sync-up expovariate() and gammavariate code (GH-1934)

Raymond Hettinger webhook-mailer at python.org
Mon Dec 24 01:54:28 EST 2018


https://github.com/python/cpython/commit/63d152232e1742660f481c04a811f824b91f6790
commit: 63d152232e1742660f481c04a811f824b91f6790
branch: master
author: leodema <leodema at users.noreply.github.com>
committer: Raymond Hettinger <rhettinger at users.noreply.github.com>
date: 2018-12-23T22:54:25-08:00
summary:

bpo-30561: Sync-up expovariate() and gammavariate code (GH-1934)

files:
A Misc/NEWS.d/next/Library/2018-12-23-22-27-59.bpo-30561.PSRQ2w.rst
M Lib/random.py
M Lib/test/test_random.py

diff --git a/Lib/random.py b/Lib/random.py
index 9c2904cfd2f9..e00a02623890 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -582,10 +582,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)
 
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index 38fd8a9105ea..b35cb39e751b 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -853,28 +853,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:
         #
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 000000000000..ae99b7cb0ae0
--- /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.



More information about the Python-checkins mailing list