3

Does anyone have some thoughts on elegant code and math using python to solve the following problem?

I have two lists of numbers:

A=[83.4,108,-240.2]
B=[10.3,96.7,-5.5,-20.4,30.9,2.1,-6.1,51.5,37.7,-25,-10.7,-250.4,-14.2,56.4,-11.5,163.9,-146.6,-2.6,7.9,-13.2]

I know that the B can be divided into three lists that contain elements of B such that the three lists together contain all of the elements in B but the three lists have no overlapping elements. The sum of these three lists will add up to the three elements in A.

I can do the brute force method, which is to create all possible combinations of the elements of B into three sets, but the number of possibilities blows up very quickly with the number of elements in B. I've also looked at the knapsack problem, but that seems to require only positive values.

Chad Larson
  • 309
  • 2
  • 11

2 Answers2

3

This is indeed a variant of the subset sum problem:

In computer science, the subset sum problem is one of the important problems in complexity theory and cryptography. The problem is this: given a set (or multiset) of integers, is there a non-empty subset whose sum is zero? For example, given the set {−7, −3, −2, 5, 8}, the answer is yes because the subset {−3, −2, 5} sums to zero. The problem is NP-complete.

An equivalent problem is this: given a set of integers and an integer s, does any non-empty subset sum to s?


Proof that it's NP-complete:

The easiest way to prove that some new problem is NP-complete is first to prove that it is in NP, and then to reduce some known NP-complete problem to it.

It is in NP because it can be verified in polynomial time: given a potential solution, simply add up the numbers in the subsets and see if they correspond to the numbers in A. And, you can reduce the subset problem to this problem in polynomial time: Given set x and target sum s, let A = [s, sum(x) - s] and B = x.


It being NP-complete, there is no way to solve this quickly in the general case, using Python or otherwise:

Although any given solution to an NP-complete problem can be verified quickly (in polynomial time), there is no known efficient way to locate a solution in the first place; indeed, the most notable characteristic of NP-complete problems is that no fast solution to them is known. That is, the time required to solve the problem using any currently known algorithm increases very quickly as the size of the problem grows. As a consequence, determining whether or not it is possible to solve these problems quickly, called the P versus NP problem, is one of the principal unsolved problems in computer science today.

Community
  • 1
  • 1
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 1
    Thanks. Unfortunately, not the answer I wanted to hear. I'm not a computer scientist, but I had a feeling this was the answer to my question. – Chad Larson Sep 17 '15 at 16:40
0

As @Claudiu explained well such problems are NP complete, and you can not solve them in a efficient or general way, but in this case as a special and not much efficient way you can play itertools module like following :

>>> from itertools import combinations,product,chain

>>> length=len(B)
>>> subset_lenght=[(i,j,k) for i,j,k in combinations(range(1,length),3) if i+j+k==length]
>>> all_combinations={i:combinations(B,i) for i in range(1,length-2)}
>>> for i,j,k in subset_lenght:
...     for t,p,m in product(all_combinations[i],all_combinations[j],all_combinations[k]):
...         if not set(t)&set(p)&set(m) and map(sum,(t,p,m))==A:
...            print chain.fromiterable(t,p,m)

In this approach first of all you need all the possible lengths which those sum is equal to your main list length, for that aim you can use following list comprehension :

>>> [(i,j,k) for i,j,k in combinations(range(1,len(B)),3) if i+j+k==len(B)]
[(1, 2, 17), (1, 3, 16), (1, 4, 15), (1, 5, 14), (1, 6, 13), (1, 7, 12), (1, 8, 11), (1, 9, 10), (2, 3, 15), (2, 4, 14), (2, 5, 13), (2, 6, 12), (2, 7, 11), (2, 8, 10), (3, 4, 13), (3, 5, 12), (3, 6, 11), (3, 7, 10), (3, 8, 9), (4, 5, 11), (4, 6, 10), (4, 7, 9), (5, 6, 9), (5, 7, 8)]

Then you need to get all the combinations of the main list with the length 1 to len(main_list)-3 (17 in this case but since the range doesn't contains the last number we will put a number 1 grater) so, since we need to access this combinations with those length we can use a dict comprehension to create a dictionary with the partition length as key and the combinations as value :

>>> all_combinations={i:combinations(B,i) for i in range(1,length-2)}

And at last you need to get the the combinations based on subset_lenght items and then choose the ones that hasn't any intersection and those sum is equal to those corresponding item in A.

Mazdak
  • 105,000
  • 18
  • 159
  • 188