2

Given a list of non-negative integers say a0,a1,a2, . . . ,an and a constant positive integer m. We want to generate all lists of non-negative integers of the form a'0,a'1,a'2, . . . ,a'n such that:

  1. There exists an i such that ai ≠ a'i.
  2. For all i, we have ai ≥ a'i
  3. a0 + a1 + a2 + . . . + an - a'0 - a'1 - a'2 - . . . - a'n is a multiple of m

In terms of time, what is the best algorithm for java. I have tried two different things.

  1. For all km between 0 and the sum of the ais: First generate all partitions of km into n parts. For each of these partitions, subtract it component wise from the original sequence of integers. The resulting list is valid if all of the components are non-negative.
  2. Generate all the smaller positions and check if they satisfy condition 3.

Surprisingly, my second idea was significantly faster than the first idea but still fairly slow. How should I approach this? This question is similar in spirit to Generating the partitions of a number.

Community
  • 1
  • 1
Halbort
  • 742
  • 1
  • 6
  • 19
  • 4
    Try to post this at http://math.stackexchange.com/ as it looks more like a math problem rather than a programming problem. – Augusto Aug 08 '15 at 22:48
  • Who is `m` ? Please complete your question – Dici Aug 08 '15 at 22:50
  • 1
    This question is similar in spirit to http://stackoverflow.com/questions/400794/generating-the-partitions-of-a-number?rq=1 – Halbort Aug 08 '15 at 22:50
  • Just choose `ai = a'i for all i < n` and `a'n = an - m` – Dici Aug 08 '15 at 22:53
  • That is one possible list of integers. However, I want to find all of them. – Halbort Aug 08 '15 at 22:53
  • Isn't there an infinite number of solutions ? I can just use the process for k = 1...infinity with `a'n = an - k*m` – Dici Aug 08 '15 at 22:56
  • The a_is have to be positive – Halbort Aug 08 '15 at 22:56
  • 1
    So fix your question man, it is the third constraint you omitted in your question. Are the `ai` positive too ? Otherwise it is impossible if one `ai` is negative – Dici Aug 08 '15 at 22:57
  • Now, I don't get your first method, could you be more specific ? Note that the second one is exponential at least in time, so... maybe there is a smarter thing to do, Usually you first try to characterize mathematically the solutions or find constraints on the space of solutions to reduce the size of the space to research. – Dici Aug 08 '15 at 23:02
  • That is why I am sure there is a better algorithm. The better of the two is really slow by itself! – Halbort Aug 08 '15 at 23:04
  • Hum, actually the number of solutions is ridiculously huge (exponential), so any algorithm will be exponential – Dici Aug 08 '15 at 23:05
  • Yes but the second algorithm looks through a ridiculous amount of invalid solutions. I am wondering if there is a better way. – Halbort Aug 08 '15 at 23:09
  • What is the source of this problem? – Gassa Aug 09 '15 at 10:23

1 Answers1

2

This can be solved in polynomial time O(nm2) using dynamic programming.

Let f (i, r) be the number of ways to build a sequence a'0, a'1, ..., a'i such that

(a0 + a1, ... + ai - a'0 - a'1 - ... - a'i) mod m = r.

The choice of the first parameter is obvious: we will look at the sequence from left to right and record the result for prefixes of the sequence. The second parameter is the remainder of what we have so far modulo m, since in order to build all possible sequences, we have to consider all possible remainders in between, not just zero.

To calculate f (i, r), we will need to consider the possible choices of (ai - a'i) mod m and use the previously calculated f (i - 1, *).

  • If (ai - a'i) mod m = 0, there are u = [ai / m] + 1 such choices of a'i: these are ai, ai - m, a'i - 2 m, ..., a'i - u m. So, we must add ([ai / m] + 1) * f (i - 1, r) to our answer: for each possible answer of length i - 1 with total remainder r, we can choose each of the [ai / m] + 1 possible values of a'i such that the total remainder of the new sequence is also r.

  • If (ai - a'i) mod m = m - 1, there are v = [(ai - 1) / m] + 1 such choices of a'i: these are ai - 1, ai - 1 - m, a'i - 1 - 2 m, ..., a'i - 1 - v m. So, we must add ([(ai - 1) / m] + 1) * f (i - 1, (r + m - 1) mod m) to our answer: for each possible answer of length i - 1 with total remainder (r + m - 1) mod m, we can choose each of the [(ai - 1) / m] + 1 possible values of a'i such that the total remainder of the new sequence is now r.

  • ...

  • If (ai - a'i) mod m = 1, there are w = [(ai - (m - 1)) / m] + 1 such choices of a'i: these are ai - (m - 1), ai - (m - 1) - m, a'i - (m - 1) - 2 m, ..., a'i - (m - 1) - w m. So, we must add ([(ai - (m - 1)) / m] + 1) * f (i - 1, (r + 1) mod m) to our answer: for each possible answer of length i - 1 with total remainder (r + 1) mod m, we can choose each of the [(ai - (m - 1)) / m] + 1 possible values of a'i such that the total remainder of the new sequence is now r.

To sum that up,

f (i, r) = sum {by s = 0 to m - 1} of ([(ai - s) / m] + 1) * f (i - 1, (r + m - s) mod m).

We do that for all i and all r. The base will be f (-1, 0) = 1 (there is one empty sequence with total remainder 0) and f (-1, t) = 0 for t > 0 (there are no empty sequences with total remainder > 0). The answer will be f (n, 0) (the number of sequences a'0, a'1, ..., a'i with total remainder 0).

When implementing, take care to round integer division down and take the non-negative modulo: in languages close to CPU (like C or C++ or even Java), integer division and remainder calculation is rounded towards zero by default, so an integer result of -1 / 2 will be 0 and -1 mod 2 will be -1, when we need -1 and 1, respectively.

Community
  • 1
  • 1
Gassa
  • 8,546
  • 3
  • 29
  • 49