Unit 25: Permutations
Learning Objectives
After taking this unit, students should
- be familiar with using recursion to generate all possible permutations of a given array
Generating Permutations
We have been using recursions to either compute or find a solution to a problem. In this unit, let's look at another useful application of recursion: to generate all possible permutations or combinations of items.
Let's see a simple example of this: generate all permutations of the characters in a string of length \(n\). For simplicity, we assume that there is no repetition in the string. We leave the problem where there is repetition as an exercise in Problem 25.1.
Recursive Formulation
To formulate a recursive solution, as usual, let's simplify the problem. A simpler version of the problem is to permute a string of length \(n-1\).
The trivial case is when we generate the permutation of a string with one character. There is only one possible permutation! Now, by wishful thinking, we assume that we know how to generate all permutations of a string of length \(n-1\). Here is what we do to generate all permutations of a string of length \(n\).
For each character \(i\) in the string, we move \(i\) to the "first" position in the string. We have \(n-1\) characters left, which we solve recursively, generating all possible permutations with \(i\) as the first character.
For example, consider a string length 3, abc
.
- We start with
a
as the "first" character, and generate all the permutations of the stringbc
. We get two permutationsabc
andacb
. - The next character is
b
. We generate all permutations of the stringac
. We getbac
andbca
. - Similarly, we get the permutations
cab
andcba
by consideringc
as the "first" character and permutingba
.
Now, let's consider a longer example, say a string of length 5, abcde
.
- Just like above, we start with
a
as the first character and permutebcde
. - As we recursively permute
bcde
,a
should always remain in its position. - To recursively permute
bcde
, we first fixb
as the "first" character (butb
is the second character inabcde
) and permutecde
. - As we recursively permute
cde
,ab
should remain in their positions.
We see that, as we recursively permute the shorter string, the prefix of the string should remain fixed.
The Code
The idea above is implemented as the following. The function permute
takes in the array a
to permute, the length len
of the array, and the location curr
, where the substring a[curr]
to a[len - 1]
is what this function will permute. The function will print out all permutations of a
where a[0]
to a[curr - 1]
are fixed, and a[curr]
to a[len - 1]
are permutated.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Lines 3-6 above corresponds to the base case, where we have reached the end of the string, there is only one character left to permutate. Since there is only one possible permutation, we do not need to do anything except to print out the permutated string.
Line 8 permutes the remaining string, a[curr + 1]
to a[len - 1]
, with character a[curr]
intact. Lines 9-13 is a for loop which loops through all characters a[curr + 1]
to a[len - 1]
, and swaps each one to the position of a[curr]
, and recursively permutes the string a[curr + 1]
..a[len - 1]
. When we are done, we swap back the original a[curr]
, this is to ensure that the string remains unchanged after permute
is called.
Running Time
How efficient is the function permute
? We know that for a string of length \(n\), there are \(n!\) permutations, so the function permute
should be at least \(n!\). If it runs in \(O(n!)\) steps, then our function above is as efficient as it can get.
Let the number of times permute
is called when we pass in a string of length \(n\) be \(T(n)\). Each invocation of permute(a, n, k)
calls permute(a, n, k+1)
recursively \(n-k\) times. The first call to permute
is permute(a, n, 0)
. We stop when we call permute(a, n, n-1)
. So \(T(1) = 1\).
So, we have:
We made \(n!\) calls to permute
, so the function permute
is as efficient as it gets.
Problem Set
Problem Set 25.1
In the code above, we assume that the string contains distinct characters. If there are duplicate characters in the string, duplicate permutations will be generated. For instance, if the input is aaa
, the code above would print aaa
six times.
We can fix this by making a small change to the function permute
above so that it does not generate duplicate permutations. This can be done by adding a condition (Line A). Write a boolean function that we can call in Line A to check if we should continue to permute the rest of the string, and therefore avoid generating duplicate permutations when the input string contains duplicate characters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Appendix: Complete Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|