@@ -634,11 +634,12 @@ from the combinatoric iterators in the :mod:`itertools` module
634634or the :pypi: `more-itertools ` project:
635635
636636.. testcode ::
637+
637638 import random
638639
639- def random_product(*args , repeat=1):
640- "Random selection from itertools.product(*args, **kwds )"
641- pools = [ tuple(pool) for pool in args] * repeat
640+ def random_product(*iterables , repeat=1):
641+ "Random selection from itertools.product(*iterables, repeat=repeat )"
642+ pools = tuple(map(tuple, iterables)) * repeat
642643 return tuple(map(random.choice, pools))
643644
644645 def random_permutation(iterable, r=None):
@@ -663,15 +664,89 @@ or the :pypi:`more-itertools` project:
663664 return tuple(pool[i] for i in indices)
664665
665666 def random_derangement(iterable):
666- "Choose a permutation where no element is in its original position."
667+ "Choose a permutation where no element stays in its original position."
667668 seq = tuple(iterable)
668669 if len(seq) < 2:
669- raise ValueError('derangements require at least two values')
670- perm = list(seq)
670+ if not seq:
671+ return ()
672+ raise IndexError('No derangments to choose from')
673+ perm = list(range(len(seq)))
674+ start = tuple(perm)
671675 while True:
672676 random.shuffle(perm)
673- if all(p != q for p, q in zip(seq, perm)):
674- return tuple(perm)
677+ if all(p != q for p, q in zip(start, perm)):
678+ return tuple([seq[i] for i in perm])
679+
680+ .. doctest ::
681+ :hide:
682+
683+ >>> import random
684+
685+
686+ >>> random.seed(8675309 )
687+ >>> random_product(' ABCDEFG' , repeat = 5 )
688+ ('D', 'B', 'E', 'F', 'E')
689+
690+
691+ >>> random.seed(8675309 )
692+ >>> random_permutation(' ABCDEFG' )
693+ ('D', 'B', 'E', 'C', 'G', 'A', 'F')
694+ >>> random_permutation(' ABCDEFG' , 5 )
695+ ('A', 'G', 'D', 'C', 'B')
696+
697+
698+ >>> random.seed(8675309 )
699+ >>> random_combination(' ABCDEFG' , 7 )
700+ ('A', 'B', 'C', 'D', 'E', 'F', 'G')
701+ >>> random_combination(' ABCDEFG' , 6 )
702+ ('A', 'B', 'C', 'D', 'F', 'G')
703+ >>> random_combination(' ABCDEFG' , 5 )
704+ ('A', 'B', 'C', 'E', 'F')
705+ >>> random_combination(' ABCDEFG' , 4 )
706+ ('B', 'C', 'D', 'G')
707+ >>> random_combination(' ABCDEFG' , 3 )
708+ ('B', 'E', 'G')
709+ >>> random_combination(' ABCDEFG' , 2 )
710+ ('E', 'G')
711+ >>> random_combination(' ABCDEFG' , 1 )
712+ ('C',)
713+ >>> random_combination(' ABCDEFG' , 0 )
714+ ()
715+
716+
717+ >>> random.seed(8675309 )
718+ >>> random_combination_with_replacement(' ABCDEFG' , 7 )
719+ ('B', 'C', 'D', 'E', 'E', 'E', 'G')
720+ >>> random_combination_with_replacement(' ABCDEFG' , 3 )
721+ ('A', 'B', 'E')
722+ >>> random_combination_with_replacement(' ABCDEFG' , 2 )
723+ ('A', 'G')
724+ >>> random_combination_with_replacement(' ABCDEFG' , 1 )
725+ ('E',)
726+ >>> random_combination_with_replacement(' ABCDEFG' , 0 )
727+ ()
728+
729+
730+ >>> random.seed(8675309 )
731+ >>> random_derangement(' ' )
732+ ()
733+ >>> random_derangement(' A' )
734+ Traceback (most recent call last):
735+ ...
736+ IndexError: No derangments to choose from
737+ >>> random_derangement(' AB' )
738+ ('B', 'A')
739+ >>> random_derangement(' ABC' )
740+ ('C', 'A', 'B')
741+ >>> random_derangement(' ABCD' )
742+ ('B', 'A', 'D', 'C')
743+ >>> random_derangement(' ABCDE' )
744+ ('B', 'C', 'A', 'E', 'D')
745+ >>> # Identical inputs treated as distinct
746+ >>> identical = 20
747+ >>> random_derangement((10 , identical, 30 , identical))
748+ (20, 30, 10, 20)
749+
675750
676751The default :func: `.random ` returns multiples of 2⁻⁵³ in the range
677752*0.0 ≤ x < 1.0 *. All such numbers are evenly spaced and are exactly
0 commit comments