Back to Blog
Python

Python itertools permutations: Usage and Performance

Learn how itertools.permutations generates ordered arrangements, controls length with r, and handles memory and performance in Python.

Pythonitertoolspermutationscombinatoricsperformance
Diagram showing permutations of three elements generated by Python itertools.permutations

The itertools.permutations function is a standard Python tool for generating ordered arrangements of elements from an iterable. It returns an iterator, so you can enumerate permutations without materializing them all at once and without writing a recursive permutation algorithm by hand.

What itertools.permutations Does

itertools.permutations(iterable, r=None) returns successive r-length permutations of elements in the iterable. If r is not specified, it defaults to the length of the iterable, producing all full-length permutations. The permutations are emitted in lexicographic order according to the order of the input iterable: if the input is sorted, the output tuples appear in sorted order.

from itertools import permutations items = ['A', 'B', 'C'] for p in permutations(items): print(p)

Output:

('A', 'B', 'C')
('A', 'C', 'B')
('B', 'A', 'C')
('B', 'C', 'A')
('C', 'A', 'B')
('C', 'B', 'A')

Each permutation is a tuple. The function returns an iterator, so it does not create the entire list in memory unless you explicitly convert it to a list.

The r Parameter: Controlling Permutation Length

When r is provided, only permutations of that length are generated. For example, permutations(items, 2) yields all ordered pairs:

for p in permutations(items, 2): print(p)

Output:

('A', 'B')
('A', 'C')
('B', 'A')
('B', 'C')
('C', 'A')
('C', 'B')

This is useful when you need arrangements of a subset of elements, such as picking a president, vice-president, and treasurer from a set of candidates. When provided, r must be an integer between 0 and the length of the iterable. If r is greater than the length, the iterator is empty; if r is 0, it yields a single empty tuple.

Understanding the Output: Tuples and Ordering

Each permutation is a tuple of elements from the input. The output ordering depends on the input iterable's order: if the input is sorted, the permutations are emitted in lexicographic order. This behavior is deterministic, which is helpful for testing and reproducibility.

The number of permutations is n! / (n - r)! for r-length permutations. For full permutations (r = n), it is n!. This grows extremely fast: 10! is 3,628,800, and 15! is over 1.3 trillion. The iterator produces results lazily, but processing all of them will still consume time proportional to the count.

Memory and Performance Considerations

Because permutations returns an iterator, it does not store all permutations in memory. Each tuple is generated on demand and can be processed or discarded. This is critical for large inputs: materializing all permutations into a list can exhaust memory quickly.

For example, list(permutations(range(10))) creates a list of 3.6 million tuples, each of length 10, which can consume hundreds of megabytes. In contrast, iterating over the iterator allows you to process each permutation without holding the entire collection.

Time complexity is proportional to the number of permutations, which is factorial. There is no way to avoid the combinatorial explosion if you need to examine every permutation. If you only need a subset, consider using islice from itertools to limit the iteration.

from itertools import permutations, islice # Process only the first 100 permutations for p in islice(permutations(range(20)), 100): # do something pass

This avoids generating all permutations when you only need a sample.

Comparing Permutations, Combinations, and Product

itertools provides three related combinatoric functions:

  • permutations(iterable, r) — ordered arrangements, no repeated elements.
  • combinations(iterable, r) — unordered selections, no repeated elements.
  • product(iterable, repeat=r) — ordered arrangements with repetition allowed.

The key difference is whether order matters and whether elements can repeat. For example, with ['A', 'B'] and r=2:

  • permutations gives ('A','B') and ('B','A').
  • combinations gives only ('A','B').
  • product gives ('A','A'), ('A','B'), ('B','A'), ('B','B').

Choosing the right function depends on the problem. If you need to assign distinct roles, use permutations. If you need to select a committee where order doesn't matter, use combinations. If repetition is allowed, such as generating all possible PIN codes, use product.

Common Mistakes and Edge Cases

One common mistake is assuming that permutations works on sets without considering that sets are unordered. If you pass a set, the order of elements is arbitrary, and the permutations will reflect that arbitrary order. For deterministic results, convert the set to a sorted list first.

Another pitfall is using permutations on a list with duplicate elements. The function treats each element as distinct, even if they have equal values. For example, permutations(['A', 'A']) yields two identical tuples: ('A', 'A') twice. If you need unique permutations, you must filter duplicates yourself, for instance by converting the result to a set (which loses order) or using a custom deduplication approach.

Also, r must be an integer. Passing a float like 2.0 raises a TypeError. Ensure r is within the valid range; otherwise, the iterator is empty.

Practical Example: Assigning Tasks to Team Members

A common use is assigning four tasks to four team members, where each member gets exactly one task and the order of assignment matters. That is a permutation of the tasks.

from itertools import permutations tasks = ['deploy', 'test', 'review', 'document'] members = ['Alice', 'Bob', 'Carol', 'Dave'] for assignment in permutations(tasks): for member, task in zip(members, assignment): print(f'{member}: {task}') print('---')

This generates all 24 possible assignments. For a larger team, the factorial growth makes brute-force enumeration impractical, so you would need optimization techniques like constraint programming or heuristics.

Performance Optimization: Avoiding Unnecessary Work

When you only need permutations that satisfy a certain condition, you can often prune the search space by generating candidates incrementally. For example, if you are solving a permutation-based puzzle, you can check partial permutations before generating the full sequence. However, itertools.permutations generates full permutations only; there is no built-in way to prune. In such cases, consider writing a recursive generator that builds permutations step by step, allowing early termination.

If you need unique permutations from an iterable with repeated elements, you can use more-itertools's distinct_permutations when third-party libraries are allowed. The standard library does not provide a direct way to handle duplicates efficiently.

When Not to Use itertools.permutations

If the number of permutations is astronomically large, iterating through all of them may be impractical. The iterator is lazy, but the total work is still factorial. For instance, generating all permutations of 20 elements would require 20! iterations, which is roughly 2.4e18 — far beyond any reasonable computation time.

In that situation, consider random sampling, Monte Carlo methods, or algorithmic shortcuts, depending on the problem. Use itertools.permutations when the problem size is manageable and you need ordered, non-repeating arrangements; for larger problems, exhaustive iteration with this function is unlikely to be practical.

Python itertools.permutations: Usage, r Parameter, and Performance | RYUSLOG DEV