permutations
list manipulation
algorithm
programming
coding techniques

How do I generate all permutations of a list?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Generating all permutations of a list means enumerating every possible ordering of its elements. In Python, the practical default is itertools.permutations, because it is correct, concise, and lazy. The harder part is usually not the syntax. It is recognizing that the number of permutations grows factorially, so generating all of them can become infeasible very quickly.

Use itertools.permutations First

For most real code, the standard library already provides the correct tool.

python
1from itertools import permutations
2
3items = [1, 2, 3]
4for p in permutations(items):
5    print(p)

This yields tuples such as (1, 2, 3) and (3, 2, 1). If you need lists instead of tuples, convert them explicitly.

python
1from itertools import permutations
2
3items = [1, 2, 3]
4all_as_lists = [list(p) for p in permutations(items)]
5print(all_as_lists)

That covers the majority of use cases cleanly.

Understand the Growth Before You Materialize

A list of length n has n! permutations.

  • '3! is 6'
  • '5! is 120'
  • '8! is 40320'
  • '10! is 3628800'

That is why the best implementation is often the lazy one. If you only need to scan permutations until one matches a condition, do not convert the iterator to a list. Iterate and stop early.

python
1from itertools import permutations
2
3for p in permutations([1, 2, 3, 4]):
4    if p[0] == 4:
5        print("found", p)
6        break

This can save a large amount of memory and time.

Write a Backtracking Version Only When You Need Control

If you need custom pruning, learning value, or nonstandard behavior, a backtracking implementation is useful.

python
1def permute(items):
2    result = []
3
4    def backtrack(start):
5        if start == len(items):
6            result.append(items[:])
7            return
8
9        for i in range(start, len(items)):
10            items[start], items[i] = items[i], items[start]
11            backtrack(start + 1)
12            items[start], items[i] = items[i], items[start]
13
14    backtrack(0)
15    return result
16
17print(permute([1, 2, 3]))

This works by choosing which element should occupy each position, recursing, and then undoing the choice before trying the next branch.

That algorithm is useful when the standard iterator is not enough, but it should not be the first thing you write for an everyday permutation task.

Handle Duplicates Deliberately

If the input contains repeated values, naive generation will produce repeated permutations.

python
1from itertools import permutations
2
3items = [1, 1, 2]
4print(list(permutations(items)))

If you only need unique permutations, one simple solution is to deduplicate afterward.

python
1from itertools import permutations
2
3items = [1, 1, 2]
4unique = sorted(set(permutations(items)))
5print(unique)

That is fine for small inputs. For larger inputs with many duplicates, a custom algorithm that skips repeated choices is more efficient than generating duplicates and removing them later.

Partial Permutations Are Often Enough

Many problems do not actually need full-length permutations. itertools.permutations can generate ordered selections of length r.

python
1from itertools import permutations
2
3items = ["a", "b", "c", "d"]
4for p in permutations(items, 2):
5    print(p)

This is useful when the task is “all ordered pairs” or “all ordered triples,” not “all full rearrangements of the whole list.”

Recognizing that difference can turn an expensive problem into a manageable one.

Think About the Goal, Not Just the Tool

Permutation generation often appears inside optimization or search tasks. In those cases, the deeper question is whether you really need every ordering. Sometimes a greedy algorithm, dynamic programming approach, or constraint-based search is the better model.

It is worth asking that question early because permutations become expensive very fast. Generating them all is correct, but not always wise.

Common Pitfalls

  • Converting the entire permutation iterator to a list when lazy iteration would have been enough.
  • Forgetting that the number of permutations grows as n! and becomes infeasible quickly.
  • Reimplementing the algorithm when itertools.permutations already solves the practical problem.
  • Ignoring duplicate input values and then being surprised by repeated outputs.
  • Generating full permutations when the actual problem only needs ordered selections of smaller length.

Summary

  • In Python, itertools.permutations is the standard and usually best solution.
  • Permutations grow factorially, so feasibility matters as much as syntax.
  • Keep the result lazy unless you truly need all permutations materialized.
  • Use backtracking only when you need custom control beyond the standard library.
  • Handle duplicates and partial-length cases deliberately based on the real problem.

Course illustration
Course illustration

All Rights Reserved.