# Generate successive k-length permutations. (4.01) # @note Rewrite of the code that appears at # docs.python.org/3/library/itertools.html#itertools.permutations # (eliminates indirect indexing; derive from direct if needed). # @see 3UOMj3, vSELdo, cTaz9y def permutations(iterable, k=None): items = list(iterable) n = len(items) if k is None: k = n elif k > n: return elif k < 0: raise ValueError('k must be non-negative') cycle = list(range(k)) yield tuple(items[:k]) i = k-1 while i >= 0: j = cycle[i] + 1 if j < n: cycle[i] = j items[i], items[j] = items[j], items[i] yield tuple(items[:k]) i = k-1 else: cycle[i] = i items.append(items.pop(i)) i = i-1 def permutations_indirect(iterable, k=None): pool = tuple(iterable) for index in permutations(range(len(pool)), k): yield tuple(pool[i] for i in index) # Test. import itertools import time n = 3 a = list(range(n)) for k in range(2+len(a)): print(list(permutations(a, k))) print(list(itertools.permutations(a, k))) def test(f, a): for k in itertools.chain([None], range(2+len(a))): m = sum(1 for _ in f(a, k)) n = sum(1 for _ in itertools.permutations(a, k)) assert m == n for x, y in zip(f(a, k), itertools.permutations(a, k)): assert x == y t = time.process_time() for _ in f(a): pass t = time.process_time() - t print('Elapsed: {:.6f}s'.format(t)) n = 8 a = list(range(n)) test(permutations, a) test(permutations_indirect, a) test(itertools.permutations, a)
Standard input is empty
[()] [()] [(0,), (1,), (2,)] [(0,), (1,), (2,)] [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] [] [] Elapsed: 0.017115s Elapsed: 0.045164s Elapsed: 0.002101s