Problem overview
The function countPermutations takes an int n and returns the number of valley permutations of length n. A permutation arranges the numbers 1 to n, each used once. It is a valley permutation if it is a decreasing run followed by an increasing run, so the values fall to a lowest point and then rise. Either run may be empty, so a fully increasing or fully decreasing arrangement counts too. The lesson's notes say n is at most 12.
For n = 4 the answer is 8. The valley permutations are [1, 2, 3, 4], [2, 1, 3, 4], [3, 1, 2, 4], [3, 2, 1, 4], [4, 1, 2, 3], [4, 2, 1, 3], [4, 3, 1, 2] and [4, 3, 2, 1]. Out of the 24 permutations of four numbers, the other 16 go up and down more than once.
Approach: place the largest number at an end
In a valley, the smallest value is the bottom, and the values rise as you move away from it in either direction. Every value other than the bottom therefore has a larger neighbour on the side facing away from the bottom. The largest value, i, has no larger neighbour, so it can't be one of those positions. For i > 1 it is not the bottom either, so it must stand at the first or the last position, where there is no neighbour on one side.
Remove i from either end. What remains is a valley permutation of length i - 1: the shape of the remaining numbers is unchanged. Going the other way, take any valley permutation of length i - 1 and put i in front of it or after it. Both results are valleys, and they are different permutations. So each valley permutation of length i - 1 yields exactly two of length i, and the count doubles each time.
The base case is dp[1] = 1. A single number is its own valley. The length 0 is handled separately, since the table starts at 1.
Step by step
- Handle
n == 0first: return 1. The empty permutation is the only one of length 0, and it counts as a valley, since both of its parts are empty. The table below needs index 1, which does not exist forn = 0. - Create the table with
make([]int, n+1). It is filled with zeros, anddp[i]will hold the count for lengthi. - Set
dp[1] = 1. - Loop with
ifrom 2 up to and includingn. - Double:
dp[i] = 2 * dp[i-1]. - Return
dp[n].
The solution in Go
func countPermutations(n int) int {
if n == 0 {
return 1
}
dp := make([]int, n+1)
dp[1] = 1
for i := 2; i <= n; i++ {
dp[i] = 2 * dp[i-1]
}
return dp[n]
}
The Go details worth a closer look:
ifwithout parentheses. The conditionn == 0needs no parentheses, and the braces are required even for a single statement.- The early return is not optional. Without it,
dp[1] = 1on a slice of length 1 panics at run time with an index out of range error. Go checks every slice index while the program runs. make([]int, n+1). It creates a slice whose elements are all 0. A slice is the right choice for a size that is only known at run time.- Size. The answer is
2^(n-1), which forn = 12is 2048, far inside the range ofint.
Complexity
Time: O(n) for the loop. Space: O(n) for the table. Keeping a single variable instead of the table gives O(1) space, and the closed form 1 << (n - 1) is a shortcut for n >= 1.
Edge cases and common mistakes
n = 0. Returns 1 through the early check.n = 1. The loop does not run, anddp[1]is 1.n = 2. Both[1, 2]and[2, 1]are valleys, and the function returns 2.- Expecting
2^n. The table starts at 1 forn = 1, so the result is2^(n-1). Startingdp[1]at 2 would be wrong: forn = 1there is only one arrangement. - Counting the largest value in the middle. A permutation such as
[1, 3, 2]goes up and then down, which is the opposite shape, a peak. It is not a valley. - Using the factorial instead. For
n = 4, 24 would be the count of all permutations, not those with the valley shape.
Testing
n = 0gives1.n = 1gives1.n = 2gives2.n = 4gives8.n = 12gives2048.