@@ -47,7 +47,7 @@ Iterator Arguments Results
4747Iterator Arguments Results Example
4848============================ ============================ ================================================= =============================================================
4949:func: `accumulate ` p [,func] p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) → 1 3 6 10 15 ``
50- :func: `batched ` p, n (p0, p1, ..., p_n-1), ... ``batched('ABCDEFG', n=2 ) → AB CD EF G ``
50+ :func: `batched ` p, n (p0, p1, ..., p_n-1), ... ``batched('ABCDEFG', n=3 ) → ABC DEF G ``
5151:func: `chain ` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') → A B C D E F ``
5252:func: `chain.from_iterable ` iterable p0, p1, ... plast, q0, q1, ... ``chain.from_iterable(['ABC', 'DEF']) → A B C D E F ``
5353:func: `compress ` data, selectors (d[0] if s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) → A C E F ``
@@ -181,7 +181,7 @@ loops that truncate the stream.
181181 Roughly equivalent to::
182182
183183 def batched(iterable, n, *, strict=False):
184- # batched('ABCDEFG', 2 ) → AB CD EF G
184+ # batched('ABCDEFG', 3 ) → ABC DEF G
185185 if n < 1:
186186 raise ValueError('n must be at least one')
187187 iterator = iter(iterable)
@@ -842,7 +842,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
842842 from contextlib import suppress
843843 from functools import reduce
844844 from math import comb, prod, sumprod, isqrt
845- from operator import itemgetter, getitem, mul, neg
845+ from operator import is_not, itemgetter, getitem, mul, neg
846846
847847 def take(n, iterable):
848848 "Return first n items of the iterable as a list."
@@ -978,6 +978,16 @@ and :term:`generators <generator>` which incur interpreter overhead.
978978 slices = starmap(slice, combinations(range(len(seq) + 1), 2))
979979 return map(getitem, repeat(seq), slices)
980980
981+ def derangements(iterable, r=None):
982+ "Produce r length permutations without fixed points."
983+ # derangements('ABCD') → BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA
984+ # Algorithm credited to Stefan Pochmann
985+ seq = tuple(iterable)
986+ pos = tuple(range(len(seq)))
987+ have_moved = map(map, repeat(is_not), repeat(pos), permutations(pos, r=r))
988+ valid_derangements = map(all, have_moved)
989+ return compress(permutations(seq, r=r), valid_derangements)
990+
981991 def iter_index(iterable, value, start=0, stop=None):
982992 "Return indices where a value occurs in a sequence or iterable."
983993 # iter_index('AABCADEAF', 'A') → 0 1 4 7
@@ -1663,6 +1673,36 @@ The following recipes have a more mathematical flavor:
16631673 ['A', 'AB', 'ABC', 'ABCD', 'B', 'BC', 'BCD', 'C', 'CD', 'D']
16641674
16651675
1676+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' )))
1677+ 'BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA'
1678+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 3 )))
1679+ 'BAD BCA BCD BDA CAB CAD CDA CDB DAB DCA DCB'
1680+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 2 )))
1681+ 'BA BC BD CA CD DA DC'
1682+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 1 )))
1683+ 'B C D'
1684+ >>> ' ' .join(map (' ' .join, derangements(' ABCD' , 0 )))
1685+ ''
1686+ >>> # Compare number of derangements to https://oeis.org/A000166
1687+ >>> [len (list (derangements(range (n)))) for n in range (10 )]
1688+ [1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496]
1689+ >>> # Verify that identical objects are treated as unique by position
1690+ >>> identical = ' X'
1691+ >>> distinct = ' x'
1692+ >>> seq1 = (' A' , identical, ' B' , identical)
1693+ >>> result1 = ' ' .join(map (' ' .join, derangements(seq1)))
1694+ >>> result1
1695+ 'XAXB XBXA XXAB BAXX BXAX BXXA XAXB XBAX XBXA'
1696+ >>> seq2 = (' A' , identical, ' B' , distinct)
1697+ >>> result2 = ' ' .join(map (' ' .join, derangements(seq2)))
1698+ >>> result2
1699+ 'XAxB XBxA XxAB BAxX BxAX BxXA xAXB xBAX xBXA'
1700+ >>> result1 == result2
1701+ False
1702+ >>> result1.casefold() == result2.casefold()
1703+ True
1704+
1705+
16661706 >>> list (powerset([1 ,2 ,3 ]))
16671707 [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
16681708 >>> all (len (list (powerset(range (n)))) == 2 ** n for n in range (18 ))
0 commit comments