最大子集,其总和小于给定总和

时间:2016-12-22 14:03:35

标签: python arrays algorithm powerset

列表定义如下:[1, 2, 3]

及其子列表是:

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

给定示例3的K,任务是找到子列表的最大长度,其中元素之和小于等于k。

我知道python中的itertools但是它会导致较大列表的分段错误。有没有其他有效的算法来实现这一目标?任何帮助,将不胜感激。

我的代码是允许的:

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

4 个答案:

答案 0 :(得分:1)

据我所知(因为您将子数组视为初始数组的任何项),您可以使用 greedy 算法,其复杂度为O(N*log(N))(你必须对数组进行排序):

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

实施例

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

解决方案

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       

作为替代方案,您可以从子数组开始,并将项目从最小值添加到最大值,而总和小于或等于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]           

答案 1 :(得分:0)

看,为了产生功率设置需要O(2 ^ n)时间。这很糟糕。您可以使用动态编程方法。

在此处查看算法。 http://www.geeksforgeeks.org/dynamic-programming-subset-sum-problem/

是的,https://www.youtube.com/watch?v=s6FhG--P7z0(Tushar很好地解释了一切):D

答案 2 :(得分:0)

您可以使用@Apy链接到的dynamic programming solution。这是一个Python示例:

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)))

输出:

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

在上面arr[i][j] True,如果设置值为i,则可以从items[0...j-1]中选择。自然arr[0]只包含True个值,因为可以选择空集。类似地,对于所有连续的行,第一个单元格是False,因为不能存在具有非零值的空集。

对于其余的细胞,有两种选择:

  1. 如果已经存在值为i的子集,即使不考虑item[j-1],则值为True
  2. 如果某个子集的值为i - items[j - 1],那么我们可以向其中添加项目,并使用值为i的子集。

答案 3 :(得分:0)

假设一切都是积极的。 (处理底片是对此的简单扩展,留给读者作为练习)。对于所描述的问题,存在O(n)算法。使用O(n)中位数选择,我们根据中位数对数组进行分区。我们找到左侧的总和。如果该值大于k,则我们不能取所有元素,因此必须在左半部分重新尝试取较小的元素。否则,我们从k中减去左半部分的总和,然后在右半部分上递归,以查看可以容纳多少个元素。

基于中位数选择对数组进行分区并仅对其中一半进行重复操作,可得到n + n / 2 + n / 4 + n / 8 ..的运行时间,其几何总和为O(n)。