Наибольшее подмножество, сумма которого меньше заданной суммы

Список определяется следующим образом: [1, 2, 3]

и подсписки этого:

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

Учитывая K, например 3, задача состоит в том, чтобы найти наибольшую длину подсписка, сумма элементов которого меньше, чем равна k.

Я знаю о itertools в python, но это приведет к ошибке сегментации для больших списков. Есть ли другой эффективный алгоритм для достижения этого? Любая помощь будет оценена по достоинству.

Мой код позволяет:

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

person Aditya    schedule 22.12.2016    source источник
comment
Каков размер, связанный с длиной исходного массива?   -  person ilim    schedule 22.12.2016
comment
Название вопроса должно быть самым большим подмножеством. Не силовой набор.   -  person lu5er    schedule 22.12.2016
comment
Нет определенной границы размера, для которого определяется проблема. Я считаю, что алгоритм должен учитывать это сам по себе.   -  person Aditya    schedule 22.12.2016
comment


Ответы (4)


Вы можете использовать решение для динамического программирования, на которое ссылается @Apy. Вот пример 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.
person niemmi    schedule 22.12.2016
comment
не могли бы вы объяснить, что здесь происходит? - person Aditya; 22.12.2016
comment
@Aditya Добавлены комментарии к коду и краткое объяснение. - person niemmi; 22.12.2016

Насколько я вижу (поскольку вы рассматриваете подмассив как любые элементы исходного массива), вы можете использовать жадный алгоритм со сложностью 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]           
person Dmitry Bychenko    schedule 22.12.2016
comment
В вашем решении есть подмассив, например [1,1,2,3,4], если весь массив равен [1,1,2,3,4,5,10,25] sum=11 ‹=12 является решением, которое необходимо, поскольку этот подмассив имеет наибольшую длину. - person Aditya; 22.12.2016
comment
@Aditya: прости, не следую за тобой. Каков тогда ожидаемый результат? Да, в случае [1,1,2,3,4,5,10,25] и k = 12 подмассив [1,1,2,3,4] имеет наибольшую длину, такую ​​как sum(sub array) <= k (один из, поскольку [1,1,2,3,5] — еще один пример). - person Dmitry Bychenko; 22.12.2016
comment
Но вам нужно отсортировать массив. Сложность доходит до nlogn. - person lu5er; 22.12.2016
comment
Для всех подмассивов основного массива сумма элементов подмассивов должна быть меньше или равна k, а возвращаемое значение представляет собой длину наибольшего подмассива. Да, [1,1,2,3,5] — это тоже решение, и нам просто нужно вернуть длину этого массива. - person Aditya; 22.12.2016
comment
используйте функцию len(). - person lu5er; 22.12.2016
comment
будет ли этот жадный подход работать для отрицательных весов? - person lu5er; 22.12.2016
comment
Что произойдет в этом сценарии? {3, 34, 4, 12, 5, 2}, сумма = 7 отсортированный массив = {2, 3, 4, 5, 12, 34} жадный добавит 2+3 = 5 и 2+3+4 = 9 Это тогда придется отступить, да? - person lu5er; 22.12.2016
comment
@Apy: жадный подход подойдет для любых предметов, если k >= 0; в случае k < 0 подойдет только удаление реализации (но не альтернатива, которая добавляется) - person Dmitry Bychenko; 22.12.2016
comment
@Apy: {3, 34, 4, 12, 5, 2}, sum = 9 возвращает [2, 3, 4] - person Dmitry Bychenko; 22.12.2016
comment
Ok. Скажи мне за {3,34,4,12,5,2} сумма = 7. @DmitryBychenko, я только учусь, поэтому задаю вопросы. Надеюсь, ты не против. :) - person lu5er; 22.12.2016
comment
@Apy: {3,34,4,12,5,2} sum = 7 возвращает {2, 3} - person Dmitry Bychenko; 22.12.2016
comment
Но сумма = 7. Подмножество {2, 3} не может предоставить необходимую информацию. Не так ли? - person lu5er; 22.12.2016
comment
подмассивы, которые удовлетворяют этому условию: `[3], [4], [5], [2], [3,2], [3,4], [2,5], [4,6]. Самая длинная длина этих подмассивов равна 2, поэтому ответ равен 2. Надеюсь, это поможет. - person Aditya; 22.12.2016
comment
@Apy: насколько я вижу, это самый последний (один из {2, 4}, {3, 4} являются альтернативами) подмассив (2 элементов), сумма которых меньше или равна 7. Какую информацию должно предоставлять подмножество? - person Dmitry Bychenko; 22.12.2016
comment
Апи, да длина не проблема, просто помогаю @Dmitry понять, что нужно. - person Aditya; 22.12.2016

Посмотрите, для генерации набора мощности требуется время O(2^n). Это очень плохо. Вместо этого вы можете использовать подход динамического программирования.

Загляните сюда, чтобы узнать алгоритм. http://www.geeksforgeeks.org/dynamic-programming-subset-sum-problem/

И да, https://www.youtube.com/watch?v=s6FhG--P7z0 (Тушар все хорошо объясняет) :D

person lu5er    schedule 22.12.2016
comment
Я посмотрю на это. - person Aditya; 22.12.2016
comment
Тушар пришел на помощь? :D - person lu5er; 22.12.2016
comment
на самом деле не потому, что сумма элементов должна быть меньше или равна. Решение на гике для гиков, которое я уже проверяю на точную сумму. Хотя все равно ищу. - person Aditya; 22.12.2016
comment
@Aditya Можете ли вы дать мне несколько тестовых случаев? - person lu5er; 22.12.2016
comment
[1], [2], [3]. [2,3], [1,3], [1,2], [1,2,3] k =3 окончательный ответ равен 2, так как [1,2] имеет сумму 3 и наибольшую длину. - person Aditya; 23.12.2016
comment
[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 ответ равен 3, так как [1,1,2] — это самый большой подмассив, сумма которого равна ‹=4. Надеюсь это поможет. Дай мне знать. - person Aditya; 23.12.2016
comment
не могли бы вы сообщить мне, если вы смогли получить это? - person Aditya; 25.12.2016
comment
@ Адитья, прости. Я совсем забыл про твой пост. Занялся своим выпускным проектом. Просто дай мне день. Я сделаю это к завтрашнему дню. :) - person lu5er; 25.12.2016

Предположим, что все положительно. (Обработка негативов является простым расширением этого и предоставляется читателю в качестве упражнения). Существует алгоритм O(n) для описанной задачи. Используя выбор медианы O(n), мы разбиваем массив на основе медианы. Находим сумму левой части. Если это больше, чем k, то мы не можем взять все элементы, поэтому мы должны вернуться к левой половине, чтобы попытаться взять меньший набор. В противном случае мы вычитаем сумму левой половины из k, затем возвращаемся к правой половине, чтобы посмотреть, сколько еще элементов мы можем взять.

Разделение массива на основе медианного выбора и повторение только одной из половин дает время выполнения n + n/2 + n/4 + n/8.., что геометрически суммируется до O (n).

person Confused Soul    schedule 15.02.2020