2

A list is defined as follows: [1, 2, 3]

and the sub-lists of this are:

[1], [2], [3],  
[1,2]  
[1,3]
[2,3]  
[1,2,3]

Given K for example 3 the task is to find the largest length of sublist with sum of elements is less than equal to k.

I am aware of itertools in python but it will result in segmentation fault for larger lists. Is there any other efficient algorithm to achieve this? Any help would be appreciated.

My code is as allows:

from itertools import combinations
def  maxLength(a, k):
#print a,k
l= []

i = len(a)
while(i>=0):
    lst= list(combinations(sorted(a),i))
    for j in lst:
        #rint list(j)
        lst = list(j)
        #print sum(lst)
        sum1=0
        sum1 = sum(lst)
        if sum1<=k:
            return len(lst)
    i=i-1
wim
  • 338,267
  • 99
  • 616
  • 750
Aditya
  • 93
  • 2
  • 12
  • What is the size bound for the length of the initial array? – ilim Dec 22 '16 at 14:12
  • The title of the question should be largest subset. Not power-set. – lu5er Dec 22 '16 at 14:16
  • There is no definite size bound for which the problem is defined. I believe the algorithm should take this into consideration on its own. – Aditya Dec 22 '16 at 14:16
  • Possible duplicate of [How to find all combinations that sum up to at most a constant?](https://stackoverflow.com/questions/46942681/how-to-find-all-combinations-that-sum-up-to-at-most-a-constant) – wim Jan 07 '19 at 00:28

4 Answers4

4

You can use the dynamic programming solution that @Apy linked to. Here's a Python example:

def largest_subset(items, k):
    res = 0

    # We can form subset with value 0 from empty set,
    # items[0], items[0...1], items[0...2]
    arr = [[True] * (len(items) + 1)]

    for i in range(1, k + 1):
        # Subset with value i can't be formed from empty set
        cur = [False] * (len(items) + 1)

        for j, val in enumerate(items, 1):
            # cur[j] is True if we can form a set with value of i from
            # items[0...j-1]
            # There are two possibilities
            # - Set can be formed already without even considering item[j-1]
            # - There is a subset with value i - val formed from items[0...j-2]
            cur[j] = cur[j-1] or ((i >= val) and arr[i-val][j-1])
        if cur[-1]:
            # If subset with value of i can be formed store
            # it as current result
            res = i

        arr.append(cur)
    return res

ITEMS = [5, 4, 1]
for i in range(sum(ITEMS) + 1):
    print('{} -> {}'.format(i, largest_subset(ITEMS, i)))

Output:

0 -> 0
1 -> 1
2 -> 1
3 -> 1
4 -> 4
5 -> 5
6 -> 6
7 -> 6
8 -> 6
9 -> 9
10 -> 10

In above arr[i][j] is True if set with value of i can be chosen from items[0...j-1]. Naturally arr[0] contains only True values since empty set can be chosen. Similarly for all the successive rows the first cell is False since there can't be empty set with non-zero value.

For rest of the cells there are two options:

  1. If there already is a subset with value of i even without considering item[j-1] the value is True
  2. If there is a subset with value of i - items[j - 1] then we can add item to it and have a subset with value of i.
niemmi
  • 17,113
  • 7
  • 35
  • 42
3

As far as I can see (since you treat sub array as any items of the initial array) you can use greedy algorithm with O(N*log(N)) complexity (you have to sort the array):

1. Assign entire array to the sub array
2. If sum(sub array) <= k then stop and return sub array
3. Remove maximim item from the sub array
4. goto 2

Example

[1, 2, 3, 5, 10, 25] 
 k = 12

Solution

sub array = [1, 2, 3, 5, 10, 25], sum = 46  > 12, remove 25
sub array = [1, 2, 3, 5, 10],     sum = 21  > 12, remove 10
sub array = [1, 2, 3, 5],         sum = 11 <= 12, stop and return       

As an alternative you can start with an empty sub array and add up items from minimum to maximum while sum is less or equal then k:

sub array = [],               sum =  0 <= 12, add 1
sub array = [1],              sum =  1 <= 12, add 2          
sub array = [1, 2],           sum =  3 <= 12, add 3             
sub array = [1, 2, 3],        sum =  6 <= 12, add 5             
sub array = [1, 2, 3, 5],     sum = 11 <= 12, add 10             
sub array = [1, 2, 3, 5, 10], sum = 21 >  12, stop, 
                              return prior one: [1, 2, 3, 5]           
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • The thing in your solution is the sub array for example [1,1,2,3,4] assuming the entire array is [1,1,2,3,4,5,10,25] sum=11 <=12 is the solution that is needed since this sub-array has the largest length. – Aditya Dec 22 '16 at 14:41
  • @Aditya: I'm sorry, don't follow you. What is the expected result then? Yes, in case of `[1,1,2,3,4,5,10,25]` and `k = 12` the sub array `[1,1,2,3,4]` is the longest length one such that `sum(sub array) <= k` (one of, since `[1,1,2,3,5]` is another example). – Dmitry Bychenko Dec 22 '16 at 14:46
  • But you have to sort the array. Complexity goes up to nlogn. – lu5er Dec 22 '16 at 14:51
  • For all the sub-arrays of the main array, the sum of elements of the sub-arrays must be less than or equal to k and the return value is a length of the largest sub array. Yes, [1,1,2,3,5] is also a solution and we just need to return the length of this array. – Aditya Dec 22 '16 at 14:53
  • 1
    use the len() function. – lu5er Dec 22 '16 at 14:54
  • will this greedy approach work for negative weights? – lu5er Dec 22 '16 at 14:56
  • What will happen in this scenario? {3, 34, 4, 12, 5, 2}, sum = 7 sorted array = {2, 3, 4, 5, 12, 34} greedy will add 2+3 = 5 and 2+3+4 = 9 It will have to backtrack then, right? – lu5er Dec 22 '16 at 15:01
  • 1
    @Apy: greedy approach will do for any items if `k >= 0`; in case `k < 0` only removing implementation will do (but not an alternative which adds up) – Dmitry Bychenko Dec 22 '16 at 15:01
  • @Apy: `{3, 34, 4, 12, 5, 2}, sum = 9` returns `[2, 3, 4]` – Dmitry Bychenko Dec 22 '16 at 15:03
  • Ok. Tell me for {3,34,4,12,5,2} sum = 7. @DmitryBychenko, I'm just learning that's why cross-questioning. Hope you don't mind. :) – lu5er Dec 22 '16 at 15:07
  • @Apy: `{3,34,4,12,5,2} sum = 7` returns `{2, 3}` – Dmitry Bychenko Dec 22 '16 at 15:08
  • But sum = 7. The subset {2, 3} fails to provide the necessary information. Isn't it? – lu5er Dec 22 '16 at 15:10
  • the sub arrays which satisfy this condition are `[3], [4], [5], [2], [3,2], [3,4], [2,5], [4,6]. The longest length of these sub arrays is 2 and hence the answer is 2. Hope this helps. – Aditya Dec 22 '16 at 15:11
  • @Apy: as far as I can see it's the lasgest (one of `{2, 4}`, `{3, 4}` are alternatives) subarray (`2` items) which sum less or equal to `7`. What kind of information should the subset provide? – Dmitry Bychenko Dec 22 '16 at 15:13
  • Apy, yes the length is not a problem, just helping @Dmitry in understanding what is needed. – Aditya Dec 22 '16 at 15:16
1

Look, for generating the power-set it takes O(2^n) time. It's pretty bad. You can instead use the dynamic programming approach.

Check in here for the algorithm. http://www.geeksforgeeks.org/dynamic-programming-subset-sum-problem/

And yes, https://www.youtube.com/watch?v=s6FhG--P7z0 (Tushar explains everything well) :D

lu5er
  • 3,229
  • 2
  • 29
  • 50
  • I'll have a look into it. – Aditya Dec 22 '16 at 14:46
  • Tushar came to the rescue? :D – lu5er Dec 22 '16 at 15:11
  • 1
    actually not cause the sum of the elements must less than or equal to. The solution on geek for geeks which I already checks for the exact sum. Still looking though. – Aditya Dec 22 '16 at 16:44
  • @Aditya Can you give me some test cases? – lu5er Dec 22 '16 at 17:48
  • `[1], [2], [3]. [2,3], [1,3], [1,2], [1,2,3] k =3` the final answer is 2 since [1,2] has the sum to be 3 and with the largest length. – Aditya Dec 23 '16 at 01:21
  • `[1], [1], [2], [3],[4], [1,2], [1,3], [2,3], [1,2,3], [1,2,1,3],[2,4,3], [1,1,3], [1,1,2], [1,1,2,3,4]..etc k=4` the answer is 3 since [1,1,2] is the largest sub-array whose sum is <=4. Hope this helps. Let me know. – Aditya Dec 23 '16 at 01:26
  • could you please let me know if you were able to get this? – Aditya Dec 25 '16 at 13:18
  • @Aditya, I'm sorry. I totally forgot about your post. Got busy with my final year project. Just give me a day. I'll get this done by tomorrow. :) – lu5er Dec 25 '16 at 16:42
0

Assume everything is positive. (Handling negatives is a simple extension of this and is left to the reader as an exercise). There exists an O(n) algorithm for the described problem. Using the O(n) median select, we partition the array based on the median. We find the sum of the left side. If that is greater than k, then we cannot take all elements, we must thus recur on the left half to try to take a smaller set. Otherwise, we subtract the sum of the left half from k, then we recur on the right half to see how many more elements we can take.

Partitioning the array based on median select and recurring on only 1 of the halves yields a runtime of n+n/2 +n/4 +n/8.. which geometrically sums up to O(n).

Confused Soul
  • 358
  • 2
  • 12