Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Generating permutations

Open In Colab

Generating permutations

In this post, we will discuss the problem of generating all permutations of a given set of elements. A permutation is an arrangement of elements in a specific order. The table below shows all permutations of the set {1, 2, 3}:

Permutations of {1, 2, 3}
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

For a set of nn elements, there are n!n! permutations. The problem of generating all permutations of a set of elements is a common problem in computer science and has applications in various fields, such as combinatorial optimization, cryptography, and data analysis.

We generate the permutations of {1,2,…,n}\{1, 2, \ldots, n\}, and use a small helper to print a permutation compactly, as in the slides: [1, 3, 2] is printed as 132.

1. The minimal-change algorithm

The minimal-change algorithm is a decrease-by-one algorithm. To generate the n!n! permutations of {1,…,n}\{1, \ldots, n\}, it first generates the (n−1)!(n-1)! permutations of {1,…,n−1}\{1, \ldots, n-1\}, then inserts nn in each of the nn possible positions of every one of them.

The order of the insertions matters. We insert nn from right to left into the first permutation, then switch direction every time we move on to the next permutation of {1,…,n−1}\{1, \ldots, n-1\}. With this rule, each permutation is obtained from the previous one by swapping two adjacent elements, which is why the algorithm is called minimal-change.

An example of generating all permutations of 3 elements:

  • start with one element: 1

  • insert 2 into 1 from right to left: 12,2112, 21

  • insert 3 into 12 from right to left: 123,132,312123, 132, 312

  • insert 3 into 21 from left to right: 321,231,213321, 231, 213

1 1
2 12 21
3 123 132 312 321 231 213
4 1234 1243 1423 4123 4132 1432 1342 1324 3124 3142 3412 4312 4321 3421 3241 3214 2314 2341 2431 4231 4213 2413 2143 2134

We can check the minimal-change property: consecutive permutations differ by a swap of two adjacent elements.

120 permutations, each one an adjacent swap away from the next

2. The Johnson-Trotter algorithm

The minimal-change algorithm needs all the permutations of {1,…,n−1}\{1, \ldots, n-1\} before it can produce the permutations of {1,…,n}\{1, \ldots, n\}. The Johnson-Trotter algorithm produces the same kind of list without generating the smaller permutations: it defines a set of rules that determine the next permutation from the current one.

  • Each element has a direction, shown by an arrow above it: it points to the left or to the right, as in 3→  2←  4→  1←\overrightarrow{3}\;\overleftarrow{2}\;\overrightarrow{4}\;\overleftarrow{1}.

  • An element is mobile if its arrow points to a smaller adjacent element. In the example above, 3 and 4 are mobile, while 2 and 1 are not.

  • To move an element, we swap it with the adjacent element its arrow points to. The arrows move with the elements.

JohnsonTrotter(n):
    initialize the first permutation with ←1 ←2 ... ←n
    while the last permutation has a mobile element do
        find its largest mobile element k
        swap k with the adjacent element k's arrow points to
        reverse the direction of all the elements that are larger than k
        add the new permutation to the list

Let’s take the example of generating all permutations of 3 elements:

  • start with the permutation 1←  2←  3←\overleftarrow{1}\;\overleftarrow{2}\;\overleftarrow{3}

  • find the largest mobile element: 3 (there are two mobile elements, 3 and 2, and we take the largest one)

  • swap 3 with the element it is pointing to: 1←  3←  2←\overleftarrow{1}\;\overleftarrow{3}\;\overleftarrow{2}

  • change the direction of all elements greater than 3: no change, since there is no element greater than 3

  • repeat the process until there is no mobile element.

The following table summarizes the steps of the Johnson-Trotter algorithm for 3 elements:

PermutationLargest mobile elementDirection change
1←  2←  3←\overleftarrow{1}\;\overleftarrow{2}\;\overleftarrow{3}3none
1←  3←  2←\overleftarrow{1}\;\overleftarrow{3}\;\overleftarrow{2}3none
3←  1←  2←\overleftarrow{3}\;\overleftarrow{1}\;\overleftarrow{2}23→\overrightarrow{3}
3→  2←  1←\overrightarrow{3}\;\overleftarrow{2}\;\overleftarrow{1}3none
2←  3→  1←\overleftarrow{2}\;\overrightarrow{3}\;\overleftarrow{1}3none
2←  1←  3→\overleftarrow{2}\;\overleftarrow{1}\;\overrightarrow{3}no mobile element

In the implementation, the directions are stored in a list direction next to the permutation: direction[i] is -1 if the element at position i points to the left and +1 if it points to the right, so i + direction[i] is the position of the element it points to.

Tracing the algorithm for n=3n = 3 reproduces the table above:

←1 ←2 ←3   largest mobile: 3
←1 ←3 ←2   largest mobile: 3
←3 ←1 ←2   largest mobile: 2
→3 ←2 ←1   largest mobile: 3
←2 →3 ←1   largest mobile: 3
←2 ←1 →3   no mobile element
123 132 312 321 231 213

Comparing the two algorithms

Both algorithms generate every permutation exactly once. They even generate them in the same order, so the Johnson-Trotter list is also a minimal-change list:

Same order for n = 1, ..., 7
1234 1243 1423 4123 4132 1432 1342 1324 3124 3142 3412 4312 4321 3421 3241 3214 2314 2341 2431 4231 4213 2413 2143 2134

The difference is in what each algorithm needs to keep. The minimal-change algorithm builds the complete list of permutations of {1,…,n−1}\{1, \ldots, n-1\} before producing anything. Johnson-Trotter only needs the current permutation and its arrows to compute the next one; the function above collects the permutations into a list only so we can print them. Finding the largest mobile element scans the permutation, so each step takes O(n)O(n) time and the whole algorithm runs in O(n⋅n!)O(n \cdot n!).