Skip to content

Instantly share code, notes, and snippets.

@chrismilson
Last active September 26, 2020 04:40
Show Gist options
  • Save chrismilson/3c738c3ebe0d84d4436246f36d0c726e to your computer and use it in GitHub Desktop.
Save chrismilson/3c738c3ebe0d84d4436246f36d0c726e to your computer and use it in GitHub Desktop.
MPMP14 - The Card Order Puzzle

This puzzle is the fourteenth puzzle from the Matt Parker's Maths Puzzles series.

Card Order Puzzle

Submittable Puzzle

If you have four cards numbered 1 to 4, how many of the 24 permutations contain no subsequence of length three that is increasing or decreasing?

Solution

Since there are only 24 different permutations, checking them with a brute force is viable. For each permutation, we count the length of the longest increasing subsequence, and the length of the longest decreasing subsequence, and if they are both less than three we can increment our counter.

Finding the length of the longest increasing subsequence can be done as follows with a dynamic programming style solve:

from bisect import bisect

def longestIncreasing(sequence):
    """
    Calculates the length of the longest increasing subsequence of a given
    sequence.
    """
    dp = [sequence[0]]

    for v in sequence:
        try:
            # bisect will do a binary search for the insertion index in dp such
            # that dp stays sorted
            dp[bisect(dp, v)] = v
        except IndexError:
            dp.append(v)

    return len(dp)

for each value in the sequence, we have to do a binary search on the dp array, whose length is bounded by the length of the sequence, giving us a runtime complecity of O(n log(n)).

Finding the length of the longest decreasing sequence is the same as finding the length of the longest increasing sequence of the sequence whose values have been negated.

We can now implement our brute force solution:

from itertools import permutations

def countSpikeyPermutations(n, k):
    """
    Counts how many permutations of the integers 1 to n contain no monotonic
    subsequences that are of length k or more.
    """
    result = 0

    for p in permutations(range(n)):
        if all(
            longestIncreasing(sequence) < k
            for sequence in (p, map(lambda x: -x, p))
        ):
            result += 1

    return result

Since there are n! different permutations, and we are calculating the lenth of the longest increasing and decreasing subseques for each of them, our total runtime complexity will be O(n!*n*log(n)). This will not scale well (it is bearable up to n ≈ 9), but we find that the number of permutations with no monotonic subsequences of length 3 or more is 4. And they are:

  • 2, 1, 4, 3.
  • 2, 4, 1, 3.
  • 3, 1, 4, 2.
  • 3, 4, 1, 2.

Extended Puzzle

If you have ten cards numbered 1 to 10, it is possible to avoid containing a monotonic subsequence of lenth 8 with the permutation:

  • 10, 2, 3, 4, 6, 5, 7, 8, 9, 1.

What is the largest k, such that every permutation is guaranteed to have either an increasing or decreasing subsequence of length k or more?

Solution

Firstly, we can repurpose our original code to have an algorithm with the same runtime complexity:

def maxUnavoidable(n):
    """
    Calculates the maximum k such that all permutations of the integers 1 to n
    must contain either an increasing or decreasing subsequence of length k or
    more.
    """
    return min(max(
        longestIncreasing(sequence)
        for sequence in (p, map(lambda x: -x, p))
    ) for p in permutations(range(n)))

This takes quite a while (calculating it on my computer took more than 30 seconds) but I was able to calculate the value of the maximum k for the first 10 integers, the number of permutations that avoid any monotonic subsequences with a length more than k + 1, and an example of one of these "optimal" permutations not containing such a subsequence:

n max k num avoiding k + 1 example "optimal" permutation
3 2 4 0, 2, 1
4 2 4 1, 0, 3, 2
5 3 86 0, 1, 4, 3, 2
6 3 306 2, 1, 4, 3, 0, 5
7 3 882 2, 5, 6, 0, 3, 1, 4
8 3 1764 3, 2, 5, 0, 7, 4, 6, 1
9 3 1764 3, 7, 2, 0, 8, 5, 6, 1, 4
10 4 985032 3, 8, 4, 0, 2, 7, 5, 1, 9, 6

We can see that the max k increases after 4 and after 9. This led me to the following proposition:

Proposition

The largest k, such that every permutation of 1, ..., n is guaranteed to have either an increasing or decreasing subsequence of length k or more is bounded above by one more than the square root of n.

Proof

We can prove this by constructing a permutation that avoids monotonic subsequences longer than the square root of p.

Let p be the smallest integer greater than or equal to the square root of n. If we take the numbers from 1 to n in order, we can reverse the sections 1 to p, p + 1 to 2p and so on until we reverse (p - 1)p to n to create a new permutation.

      .
       .
   .
    .
     .
.
 .
  .

An illustration of the scheme for n = 8.

We claim the the longest monotone subsequence has length p. Any decreasing subsequence can be no longer than p, as it must be completely contained in one of the groups. Any increasing subsequence can be no longer than p, as it cannot use more than one element from any of the groups. ☐

Proposition

The largest k, such that every permutation of 1, ..., n is guaranteed to have either an increasing or decreasing subsequence of length k or more is bounded below by the square root of n.

Proof

Fix a permutation, P, of the numbers 1 to n. For each 1 ≤ i ≤ n, we can define two numbers A(i) and D(i) where A(i) is the length of the longest increasing subsequence in P(1), P(2), ..., P(i) and and D(i) is the length of the longest decreasing subsequence in P(1), P(2), ..., P(i). We show that for every i, the pair A(i), D(i) must be unique.

Fix 1 ≤ x ≤ n. Assume that there exists some y < x such that A(y) = A(x) and D(y) = D(x). Since P is a permutation, we have P(x) ≠ P(y).

Suppose that P(y) < P(x), then there is an increasing subsequence of P(1), ..., P(y) that is increasing and has length A(y). Since P(y) < P(x) we can append x to this subsequence to generate an increasing subsequence of P(1), ..., P(x) of length A(y) + 1, but A(y) = A(x) ≥ A(y) + 1 is a contradiction.

Suppose that P(y) > P(x), then in the same manner as before, we can use a decreasing subsequence of P(1), ..., P(y) of length D(y) to produce a decreasing subsequence of P(1), ..., P(x) of length D(y) + 1, but D(y) = D(x) ≥ D(y) + 1 is a contradiction.

Therefore no such y exists, and the pair A(i), D(i) is unique for each i. We can now use the pigeon-hole principal to see that P must contain either an increasing or decreasing subsequence of the square root of n or more, since the number of unique pairs of integers bounded by b is b^2. ☐

We can now combine these propositions to form the theorem: The largest k such that every permutation of 1 to n must contain a monotonic subsequence of length k is the ceiling of the square root of n.

Firstly k must be an integer, and must be contained in [sqrt(n), sqrt(n) + 1], this is precisely the ceiling function on sqrt(n).

And now we can change our python code!

def maxUnavoidable(n):
    """
    Calculates the minimum k such that all permutations of the integers 1 to n
    must contain either an increasing or decreasing subsequence of length k or
    more.
    """
    return ceil(sqrt(n))

Much faster!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment