https://github.com/python/cpython/commit/5616ef92764c40f409d016897ce91acf2ab14f76
commit: 5616ef92764c40f409d016897ce91acf2ab14f76
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: rhettinger <[email protected]>
date: 2026-01-13T00:25:55Z
summary:
[3.13] gh-143762 Backport a6bc60d for random combinatoric recipes (GH-143764)
(gh-143766)
files:
M Doc/library/random.rst
diff --git a/Doc/library/random.rst b/Doc/library/random.rst
index dd6b56106afe98..fdc14cd04e2230 100644
--- a/Doc/library/random.rst
+++ b/Doc/library/random.rst
@@ -633,11 +633,12 @@ These recipes show how to efficiently make random
selections
from the combinatoric iterators in the :mod:`itertools` module:
.. testcode::
+
import random
- def random_product(*args, repeat=1):
- "Random selection from itertools.product(*args, **kwds)"
- pools = [tuple(pool) for pool in args] * repeat
+ def random_product(*iterables, repeat=1):
+ "Random selection from itertools.product(*iterables, repeat=repeat)"
+ pools = tuple(map(tuple, iterables)) * repeat
return tuple(map(random.choice, pools))
def random_permutation(iterable, r=None):
@@ -661,6 +662,91 @@ from the combinatoric iterators in the :mod:`itertools`
module:
indices = sorted(random.choices(range(n), k=r))
return tuple(pool[i] for i in indices)
+ def random_derangement(iterable):
+ "Choose a permutation where no element stays in its original position."
+ seq = tuple(iterable)
+ if len(seq) < 2:
+ if not seq:
+ return ()
+ raise IndexError('No derangments to choose from')
+ perm = list(range(len(seq)))
+ start = tuple(perm)
+ while True:
+ random.shuffle(perm)
+ if all(p != q for p, q in zip(start, perm)):
+ return tuple([seq[i] for i in perm])
+
+.. doctest::
+ :hide:
+
+ >>> import random
+
+
+ >>> random.seed(8675309)
+ >>> random_product('ABCDEFG', repeat=5)
+ ('D', 'B', 'E', 'F', 'E')
+
+
+ >>> random.seed(8675309)
+ >>> random_permutation('ABCDEFG')
+ ('D', 'B', 'E', 'C', 'G', 'A', 'F')
+ >>> random_permutation('ABCDEFG', 5)
+ ('A', 'G', 'D', 'C', 'B')
+
+
+ >>> random.seed(8675309)
+ >>> random_combination('ABCDEFG', 7)
+ ('A', 'B', 'C', 'D', 'E', 'F', 'G')
+ >>> random_combination('ABCDEFG', 6)
+ ('A', 'B', 'C', 'D', 'F', 'G')
+ >>> random_combination('ABCDEFG', 5)
+ ('A', 'B', 'C', 'E', 'F')
+ >>> random_combination('ABCDEFG', 4)
+ ('B', 'C', 'D', 'G')
+ >>> random_combination('ABCDEFG', 3)
+ ('B', 'E', 'G')
+ >>> random_combination('ABCDEFG', 2)
+ ('E', 'G')
+ >>> random_combination('ABCDEFG', 1)
+ ('C',)
+ >>> random_combination('ABCDEFG', 0)
+ ()
+
+
+ >>> random.seed(8675309)
+ >>> random_combination_with_replacement('ABCDEFG', 7)
+ ('B', 'C', 'D', 'E', 'E', 'E', 'G')
+ >>> random_combination_with_replacement('ABCDEFG', 3)
+ ('A', 'B', 'E')
+ >>> random_combination_with_replacement('ABCDEFG', 2)
+ ('A', 'G')
+ >>> random_combination_with_replacement('ABCDEFG', 1)
+ ('E',)
+ >>> random_combination_with_replacement('ABCDEFG', 0)
+ ()
+
+
+ >>> random.seed(8675309)
+ >>> random_derangement('')
+ ()
+ >>> random_derangement('A')
+ Traceback (most recent call last):
+ ...
+ IndexError: No derangments to choose from
+ >>> random_derangement('AB')
+ ('B', 'A')
+ >>> random_derangement('ABC')
+ ('C', 'A', 'B')
+ >>> random_derangement('ABCD')
+ ('B', 'A', 'D', 'C')
+ >>> random_derangement('ABCDE')
+ ('B', 'C', 'A', 'E', 'D')
+ >>> # Identical inputs treated as distinct
+ >>> identical = 20
+ >>> random_derangement((10, identical, 30, identical))
+ (20, 30, 10, 20)
+
+
The default :func:`.random` returns multiples of 2⁻⁵³ in the range
*0.0 ≤ x < 1.0*. All such numbers are evenly spaced and are exactly
representable as Python floats. However, many other representable
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]