Number of ways to sum the items in a set to get a target value - Order matters

牧云@^-^@ 提交于 2019-12-24 04:24:06

问题


I'm practicing for programming interviews, and I stumbled upon this question on GlassDoor: "Find the number of ways we can sum the items in the set to get our target value. Order matters."

Apparently, the interviewer couldn't come up with an answer, but he left this comment on GlassDoor: "This was more of a math question than a programming question."

This problem seems to be different than this one: Finding all possible combinations of numbers to reach a given sum.

So my questions is: what is the correct approach to solve this problem, given that order matters? And also, what would be an efficient algorithm to solve the problem of finding all the ways to sum the items in the set to reach the target value, and order matters?

If you can provide working code, that would be awesome. Also, I'm practicing in Java, so I'd prefer a Java solution, but any language would also be fine.

Thanks so much.


回答1:


Use Dynamic Programming. Let's say we have items [1, 2, 3, 4, 5] and the goal is 10. To solve this problem we will calculate the number of ways to target any value from 0 to 10 with subsets [1], [1,2].. [1,2,3,4,5].

So what if we already knew the number of ways we can sum any value from 0 to 10 with [1,2,3]? How to get the number of ways to sum any value with one more item: [1,2,3,4]? It's pretty easy: we can simple not include this new item: it's all previous values. And for every targeted value x there is an addition number of ways = number of ways targeting value (x - 4) with previous set `[1,2,3]': Let's illustrate it with table:

| sum | 1 | 2 | 3 | 4     |
|   1 | 1 | 1 |(1)| 1     |
|   2 | 0 | 1 | 1 | 1     |
|   3 | 0 | 1 | 2 | 2     |
|   4 | 0 | 0 | 1 | 1     |
|   5 | 0 | 0 |(1)|(1)+(1)| - this means that we can target 5 with 1 way
|   6 | 0 | 0 | 0 | 0 + 1 | from set `[1,2,3]` and 1 way to achieve 5 - 4 
|   7 | 0 | 0 | 0 | 0 + 2 | with the same set `[1,2,3]
|   8 | 0 | 0 | 0 | 0 + 1 |

And it's easy to get initial value. With item 1 we can target only 1.




回答2:


This is like a change-making problem, but you don't want the minimum number of "coins", you want to keep track of the number of ways. Furthermore, it is important to note that order matters. Use dynamic programming as follows. Suppose we want to show how to make 10 from sums of 1, 3, 5 where order doesn't matter (i.e. 1 + 3 + 1 + 5 is the same as 1 + 1 + 3 + 5). Make an array indexed to 10 (11 items). You can make 00 in 1 way. All negative indexes can be made in 0 ways.

00 01 02 03 04 05 06 07 08 09 10
 1

You then count the number of ways you can make 1. If you subtract coin value 1 from 1 you get 1. If you subtract any larger coin value from 1, you get a negative index which means zero ways. You sum up the results 1 way (subtracting 1) + 0 ways (subtracting 3) + 0 ways (subtracting 5). So we have

00 01 02 03 04 05 06 07 08 09 10
 1  1

Take a value of 2. We can subtract 1 cent coin to get 1 of which there is 1 way; 3 cent coin to get -1 of which there is 0 ways; 5 cent coin to get -3 of which there is 0 ways. Total 1 + 0 + 0 = 1 way to get 2 cents.

00 01 02 03 04 05 06 07 08 09 10
 1  1  1

For 3 cents, we can subtract 1 to access all the ways of making change for 2 cents, or subtract 3 cents to access all the ways of making 0 cents, or subtract 5 cents to access all the ways of making -2 cents; sum of ways is 1 + 1 + 0 = 2.

00 01 02 03 04 05 06 07 08 09 10
 1  1  1  2

For 4 cents, we can do it in 2 + 1 + 0 = 3 ways. However, note that the 3 ways include 1+1+1+1, 3+1, and 1+3. 1+3 and 3+1 are in different categories since they end in different numbers.

Continuing in this manner, we have

 00 01 02 03 04 05 06 07 08 09 10
  1  1  1  2  3  5  8 12 19 30 47

Now that we see how to construct the numbers, we can look for faster ways of doing so. Note that we have to deal with negative indices for the first few, which is a bit messy, so we might as well start with the first 5 values as given. What we need to calculate now is a recursive function: f(n) = f(n-1) + f(n-3) + f(n-5) with the condition that f(0),...,f(4) are given by the table above.

There are many ways to calculate that recursive function. This is where it becomes a real test of depth of knowledge. The naive approach would be to use recursion in the programming language. The first problem would be that your numbers will overflow because f has exponential growth. You can mitigate that situation for a time by using long instead of int. But long will quickly overflow too, so you'll have to use BigInteger to get exact results for n bigger than (I'm guessing) 20 or so.

Next, using recursive functions will be slow, because calculating f(n) might involve recalculating smaller f(x) over and over again. To mitigate that problem, use memoization to store f(x) for smaller values. Next, you'll experience stack overflow if you try to calculate from larger n to smaller. If you're calculating for a particular n, you should build up from lower values, instead of down from higher values of n.

Next, you'll experience out of memory. You can get around that by not remembering every value of f(x) as x increases, but only the last 5 values (in my example). Eventually you'll experience out of memory storing BigIntegers, but there's nothing you can do about that unless the question changes.

There are some more accelerations you can use if you're looking to improve the speed of the program. f(n) can be computed in log n operations rather than n operations in the following manner. Note that there is a matrix multiplication way of writing the recursion, where at each stage we need to remember 5 values to calculate the next one:

 |1 0 1 0 1| |f(n-1)|   | f(n) |
 |1 0 0 0 0| |f(n-2)|   |f(n-1)|
 |0 1 0 0 0| |f(n-3)| = |f(n-2)|
 |0 0 1 0 0| |f(n-4)|   |f(n-3)|
 |0 0 0 1 0| |f(n-5)|   |f(n-4)|

So we can calculate f(n) if we can calculate powers of the matrix:

 |1 0 1 0 1|^(n-4)
 |1 0 0 0 0|
 |0 1 0 0 0|
 |0 0 1 0 0|
 |0 0 0 1 0|

We can accelerate that process by using the binary expansion of n. Suppose n = 6, as it would be if we wanted f(10). Note that 6 = 4 + 2 in binary. Denote the matrix by M. We have M^1. We calculate M^2 = M^1 * M^1 and M^4 = M^2 * M^2, then M^6 = M^2 * M^4.

There may be other accelerations, depending on the particular example, but that should get you started.



来源:https://stackoverflow.com/questions/29734145/number-of-ways-to-sum-the-items-in-a-set-to-get-a-target-value-order-matters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!