Quantcast
Channel: programming – The House Carpenter
Viewing all articles
Browse latest Browse all 10

Enumerating the ordered k-partitions of an integer

$
0
0

Given two integers n and k, where k is non-negative, an ordered k-partition of n is a k-tuple (m_1, \dotsc, m_k) of positive integers such that

\displaystyle m_1 + \dotsb + m_k = n.

For example, the ordered 3-partitions of 5 are (1, 1, 3), (1, 2, 2), (1, 3, 1), (2, 1, 2), (2, 2, 1), and (3, 1, 1).

The problem I'm considering in this post is: how do you enumerate all the ordered k-partititions of n?

In the case where k = 0, the problem is simple. An ordered 0-partition is a 0-tuple. But there is only one 0-tuple: the empty tuple, (). And the sum of () is 0, which means the only integer () is an ordered 0-partition of is 0. So 0 has () as its only ordered 0-partition, while every other integer has no ordered 0-partitions.

In other cases, where k \ge 1, we can try to reduce the problem to instances of the problem where k is smaller, so that we can use recursion to arrive at a solution. Observe that an ordered k-partition of n will have the form (m_1, \dotsc, m_k), where m_1, …, m_k are positive integers that sum to n. Given that k \ge 1, we can talk about m_1 individually. By subtracting m_1 from both sides of the equation m_1 + \dotsb + m_k = n, we obtain the equivalent equation m_2 + \dotsb + m_k = n - m_1. This proves the following theorem:

Theorem. The ordered k-partitions of n are the k-tuples (m_1, \dotsc, m_k) of positive integers such that (m_2, \dotsc, m_k) is an ordered (k - 1)-partition of n - m_1.

This gives us a nice mathematical recursive characterization of the ordered k-partitions. If we write the set of all ordered k-partitions of n as P(n, k), and we denote concatenation of tuples by \frown, we can write the following equations:

\displaystyle \begin{aligned}     P(0, 0) &= \{ () \}, \\     P(n, 0) &= \varnothing &&\text{if } n \ne 0, \\     P(n, k) &= \bigcup_{m = 1}^\infty \{ (m) \frown t : t \in P(n - m, k - 1) \} &&\text{if } k \ge 1. \end{aligned}

However, these equations do not translate directly into a terminating algorithm, because of the union over all of the infinitely many positive integers m taken in the third equation.

But we don't necessarily need to examine all positive integers m, because for some of them, P(n - m, k - 1) may be empty, so that nothing is contributed to the overall union by this particular value of m. This suggests that we should consider the following sub-problem:

For which integers n and k, where k is non-negative, are there no ordered k-partitions of n?

This is easy to solve:

Theorem. For every integer n and every nonnegative integer k, the following statements are equivalent:

  • n has an ordered k-partition.
  • n = k = 0 or 1 \le k \le n.

Proof.

  • If n is negative, then it has no ordered k-partitions, because ordered k-partitions of n are tuples of positive integers that sum to n, but the sum of a tuple of positive integers is always non-negative.
  • If n < k, then n has no ordered k-partitions, because ordered k-partitions of n are tuples of k positive integers that sum to n, and the sum of k posiive integers cannot be less than k.
  • 0 has an ordered 0-partition, namely (). But no nonzero integer n has any ordered 0-partitions.
  • If 1 \le k \le n, then the ordered k-tuple consisting of k - 1 1s, followed by n - (k - 1) (which is positive since k - 1 < n), is an ordered k-partition of n.

\blacksquare

So, in the third equation above, the union only needs to be taken over values of m such that n - m = k - 1 = 0 (i.e. n = m and k = 1) or 1 \le k - 1 \le n - m (i.e. 2 \le k and m \le n - k + 1). Hence we can rewrite the equations as follows:

\displaystyle \begin{aligned}     P(0, 0) &= \{ () \}, \\     P(n, 0) &= \varnothing &&\text{if } n \ne 0, \\     P(n, 1) &= \varnothing &&\text{if } n \le 0, \\     P(n, 1) &= \{ (n) \} &&\text{if } n \ge 1, \\     P(n, k) &= \bigcup_{m = 1}^{n - k + 1} \{ (m) \frown t : t \in P(n - m, k - 1) \} &&\text{if } k \ge 2. \end{aligned}

This can now be translated straightforwardly to code in a programming language. For example, here's a Python implementation:

from typing import Iterator

def int_parts(n: int, k: int) -> Iterator[tuple[int, ...]]:
    if k == 0:
        if n == 0:
            yield ()
        return
    if k == 1:
        if n >= 1:
            yield (n,)
        return
    for m in range(1, n - k + 2):
        for p in int_parts(n - m, k - 1):
            yield (m,) + p

This is a pretty trivial subject, and I feel like my presentation of it here is somewhat incomplete (for example, I haven't said anything about how efficient the algorithm described here is). However, I think my standards for considering a post "complete" are a bit too high at the moment, as evidenced by the fact that I haven't made any posts on this blog for a couple of years, despite having written a number of unfinished drafts.

I think it would be good if whenever I learn something new, I would write down what I've learnt that very same day and make a blog post out of it, regardless of how trivial or in need of further exploration it might seem. So I'm going to attempt to do that from now on.


Viewing all articles
Browse latest Browse all 10

Trending Articles