CMPS 144 Fall 2022
Prog. Assg. #4: Subsequence Sum
Due: Nov. 28, 11:59:59pm (Deadline 48 hours later)

Background

Suppose that S and T are two sequences. Then S is said to be a subsequence of T if S can be obtained by removing zero or more of the elements in T. For example, <dog, cat, pig> is a subsequence of each of the following:

Notice that, for each of the sequences listed above, if the non-underlined elements are removed, what's left is the subsequence <dog, cat, pig>.

On the other hand, <dog, cat, pig> is a subsequence of neither <horse, dog, emu, pig, cow> nor <horse, cat, dog, emu, pig, cow>, the former because cat is missing and the latter because, even though all three of dog, cat, and pig are present, they do not appear in the correct order.

For the purposes of this assignment, we are interested in subsequences of the sequence <0, 1, 2, ..., N−1>, for some N≥0. All such sequences are thus of the form <i1, i2, ..., ik>, where 0 ≤ i1 < i2 < ..., < ik < N.

The values in the sequences we are interested in refer to locations in an array.


The Subsequence Sum Problem

An interesting combinatorial problem is the following:

Given an array A[0..N) of positive integers and a "target" value m, does there exist a subsequence <i1, i2, ..., ik> of <0,1,...,N−1> such that

A[i1] + A[i2] + ... + A[ik] = m

A problem such as this, in which the "answer" to each instance is either "YES" or "NO", is called a decision problem.

Consider the instance of the problem in which m = 8 and A is

     0    1    2    3    4    5    6    7
  +----+----+----+----+----+----+----+----+
A |  5 | 13 |  2 |  4 |  1 |  3 |  5 |  6 |
  +----+----+----+----+----+----+----+----+

The answer is "YES". Indeed, there are six distinct ways of choosing locations in A such that the sum of the elements at those locations is 8:

A[0] + A[2] + A[4] =  5 + 2 + 1 = 8
A[0] + A[5] =  5 + 3 = 8
A[3] + A[4] + A[5] =  4 + 1 + 3 = 8
A[2] + A[4] + A[6] =  2 + 1 + 5 = 8
A[5] + A[6] =  3 + 5 = 8
A[2] + A[7] =  2 + 6 = 8

This observation suggests a different version of the problem:

Given an array A[0..N) of positive integers and a "target" value m, how many distinct subsequences <i1, i2, ..., ik> of <0,1,...,N−1> are such that

A[i1] + A[i2] + ... + A[ik] = m

This kind of problem, in which the answer to each instance is a natural number describing the size of a set of "solutions", is called a counting problem.

With respect to the problem instance described above (in which m = 8 and A[] is the array in the picture), the answer is 6.

Another variation of this problem calls for a description of each solution to be produced (e.g., printed). This kind of problem is called an enumeration problem.

With respect to the problem instance described above (in which m = 8 and A[] is the array in the picture), the answer might be something like this:

<0,2,4>   <0,5>   <3,4,5>   <2,4,6>   <5,6>   <2,7>


The Logic Underlying a Solution

Consider the decision problem of interest to us:

Given array A and target m, determine whether or not there is a subsequence <i1, i2, ..., ik> of <0,1,...,N−1> (where N = A.length) satisfying A[i1] + A[i2] + ... + A[ik] = m.

As is often the case, developing a recursive solution is aided by generalizing the problem in a particular way. Here, we generalize the problem to

Given array A, integer n (0≤n≤A.length), and target m, determine whether or not there is a subsequence <i1, i2, ..., ik> of <0,1,...,n−1> satisfying A[i1] + A[i2] + ... + A[ik] = m.

That is, we generalize the problem so that each of its instances is described by a triple of parameters (A,n,m) rather than by only a pair (A,m). Indeed, the problem instance (A,m) (of the original problem) is equivalent to the problem instance (A,N,m) (where N = A.length) of the generalized problem.

Using the generalized form of the problem, we can easily describe a recursive solution to the instance (A,n,m):

The first three cases (all base cases) should be clear. As for the fourth (recursive) case, consider once again the array

     0    1    2    3    4    5    6    7
  +----+----+----+----+----+----+----+----+
A |  5 | 13 |  2 |  4 |  1 |  3 |  5 |  6 |
  +----+----+----+----+----+----+----+----+

and consider the problem instance (A,8,10), which asks whether there is any way to get 10 by choosing zero or more of the locations in A[0..8) and summing the values therein.

None of the base cases apply, so according to the recursive case, the answer is YES iff at least one among the problem instances (A,7,10) or (A,7,4) has a YES answer. (Why 4? Because if there are elements in A[0..7) that sum to 4, then those elements, plus A[7] (which is 6) sum to 10, the target.)

To say it in yet another way, it is possible to form a sum of 10 among the elements of A[0..8) iff either

  1. it is possible to form a sum of 10 among the elements of A[0..7), or
  2. it is possible to form a sum of 4 (i.e., 10−A[7]) among the elements of A[0..7).

The solutions in category (1) are those in which location 7 is not included. Those in category (2) are those that do include location 7.

As for the counting version of the problem, a recursive solution flows from the solution to the decision problem described above. The relevant observations that make the adjustment straightforward are these

As for the enumeration version of the problem, an algorithm following the same logic as described above will work. However, it is a little bit more tricky, because each time the existence of a "new" solution is verified, it is not enough to simply say return 1, as would be the case inside a method that solves the counting version of the problem. Instead, a method that enumerates solutions must have access to a "partial solution" to which it can add a location as it descends the recursion tree. When it reaches the point where it has verified the existence of a "new" solution, it can print that solution.

The mechanism by which an enumeration method can access such a partial solution is an accumulating parameter.


Assignment

Provided are the following files:


Sample User/Program Dialog

$ java SubSeqSumApp input5.txt
Array is [5, 9, 13, 6, 21, 9, 18, 25, 19, 15]

Enter target value (-1 to quit): 17

exists(17) yields false

numberOf(17) yields 0

enumerate(17) yields:

Enter target value (-1 to quit): 46

exists(46) yields true

numberOf(46) yields 11

enumerate(46) yields:
[1 [2 [3 [6 []]]]]; Sum = 46
[2 [3 [5 [6 []]]]]; Sum = 46
[4 [7 []]]; Sum = 46
[0 [1 [2 [8 []]]]]; Sum = 46
[3 [4 [8 []]]]; Sum = 46
[0 [2 [5 [8 []]]]]; Sum = 46
[1 [6 [8 []]]]; Sum = 46
[5 [6 [8 []]]]; Sum = 46
[1 [2 [5 [9 []]]]]; Sum = 46
[2 [6 [9 []]]]; Sum = 46
[3 [7 [9 []]]]; Sum = 46

Enter target value (-1 to quit): 25

exists(25) yields true

numberOf(25) yields 2

enumerate(25) yields:
[7 []]; Sum = 25
[3 [8 []]]; Sum = 25

Enter target value (-1 to quit): -2
[5, 9, 13, 6, 21, 9, 18, 25, 19, 15]

Enter target value (-1 to quit): 58

exists(58) yields true

numberOf(58) yields 19

enumerate(58) yields:
[1 [2 [3 [4 [5 []]]]]]; Sum = 58
[2 [3 [4 [6 []]]]]; Sum = 58
[0 [1 [2 [3 [7 []]]]]]; Sum = 58
[0 [2 [3 [5 [7 []]]]]]; Sum = 58
[1 [3 [6 [7 []]]]]; Sum = 58
[3 [5 [6 [7 []]]]]; Sum = 58
[0 [2 [4 [8 []]]]]; Sum = 58
[1 [4 [5 [8 []]]]]; Sum = 58
[4 [6 [8 []]]]; Sum = 58
[0 [1 [7 [8 []]]]]; Sum = 58
[0 [5 [7 [8 []]]]]; Sum = 58
[1 [2 [4 [9 []]]]]; Sum = 58
[2 [4 [5 [9 []]]]]; Sum = 58
[0 [2 [7 [9 []]]]]; Sum = 58
[1 [5 [7 [9 []]]]]; Sum = 58
[6 [7 [9 []]]]; Sum = 58
[0 [2 [3 [8 [9 []]]]]]; Sum = 58
[1 [3 [5 [8 [9 []]]]]]; Sum = 58
[3 [6 [8 [9 []]]]]; Sum = 58

Enter target value (-1 to quit): -1
Goodbye.


Submitting Your Work

Use the CMPS 144 Student File Submission/Retrieval Utility to submit your SubSeqSumSolver.java file into the prog4 folder.