总数在给定范围内的列表(作业)

时间:2019-12-17 11:11:19

标签: python algorithm list combinations

我想定义一个带有以下参数的函数: -数字列表 -最小 -最大

它应返回列表(或集合)的列表,其中每个列表的总和介于最小值和最大值之间。列表中不应有重复项。

我所得到的不会返回所有可能的列表:

def givelists(List, minimum, maximum):
    currentlist = [] #list to keep track of sum of currently included subset of list
    listoflist = [] #list of all lists of which the sum is between specified minumum and maximum
    for number in List:
        if minimum < (sum(currentlist) + number) < maximum:
            currentlist.append(number)
            listoflist.append(currentlist)
            currentlist = []
        elif (sum(currentlist) + number) < minimum:
            currentlist.append(number)
        else:
            if number in range(minimum, maximum):
                listoflist.append(number)
    return(listoflist)

例如:

givelists(list(range(1,6)), 2, 8)

# Output: [[1, 2], [3], [4], [5]]

4 个答案:

答案 0 :(得分:4)

尝试一下:

from itertools import chain, combinations

def powerset_sum(iterable, minimum, maximum):
    s = set(iterable)
    combs = chain.from_iterable(combinations(s,r) for r in range(len(s)+1))
    valid_combs = [list(comb) for comb in combs if (minimum < sum(comb) < maximum)]
    return valid_combs
print(powerset_sum(list(range(1,6)), 2, 8))

输出:

[[3],
 [4],
 [5],
 [1, 2],
 [1, 3],
 [1, 4],
 [1, 5],
 [2, 3],
 [2, 4],
 [2, 5],
 [3, 4],
 [1, 2, 3],
 [1, 2, 4]]

答案 1 :(得分:3)

您需要考虑列表中数字的多种组合,例如;

for every number in the list:
    test if that number is a feasible solution
    for every other number in the list:
        test if they can be grouped with that number \
        and produce a feasible solution # (recursion here)

所以主要是您的代码应如下所示

def sums_in_range(lst, min, max):
    result = []
    for i, number in enumerate(lst):
        if min < number < max:
            result.append([number])
        candidates = sums_in_range(
            [test for test in lst[i+1:] if test < max-number], min-number, max-number)
        for candidate in candidates:
            result.append([number]+candidate)
    return result

print(sums_in_range(sorted(list(range(1, 6))), 2, 8))

注释

  • 我正在i+1中进行lst[i+1:],因此它不会返回具有重复项(例如[1, 1, 1])的列表
  • 我正在做lst[i+1:],因此它不会计算[1, 2][2, 1]之类的重复项
  • sorted要考虑负值,请参见评论

编辑

为了完整起见(我有一些空闲时间),如果您想将事情做得更远

我已经比较了此处发布的3个答案,相同数据的存储时间和时间

,您可以找到代码来重现结果here

这就是我想出的结果

enter image description here

测试输入为

test_lst = list(range(1, 25))
test_min = 2
test_max = 50

答案 2 :(得分:1)

这是不使用导入模块的递归解决方案:

def get_lists_in_range(lst, minimum, maximum):
    result = []
    for i, item in enumerate(lst):
        #print(i, item, lst[i+1:])
        all_lists = get_lists_in_range(lst[i+1:], minimum, maximum)
        if minimum <= item <= maximum:
            result.append([item])
        for l in all_lists:
            if minimum <= item + sum(l) <= maximum:
                result.append([item] + l)           
    return result

print(get_lists_in_range(list(range(1, 6)), 2, 8))

答案 3 :(得分:0)

这是一个递归生成器函数,它执行backtracking search,并进行了一些优化。首先对列表进行排序,然后计算每个索引之后的正数和负数之和;这些可以用作剩余元素求和的界限。对于包含负数的列表,这将给出正确的答案。作为对列表进行排序的副作用,这些子集按字典顺序查找。

通常,生成器函数对于组合问题比返回列表中的所有结果更有用,因为可能有很多解决方案,并且您通常不需要一次将所有结果都存储在内存中。但是,yieldappend慢一点,因此,如果您始终希望结果显示在列表中,那么我已经显示了使用注释进行适当的更改。

def subsets_sum_in_range(lst, min_s, max_s):
    lst = sorted(lst)
    n = len(lst)
    pos_sums = [0] * (n + 1)
    neg_sums = [0] * (n + 1)
    for i in reversed(range(n)):
        pos_sums[i] = pos_sums[i+1] + max(0, lst[i])
        neg_sums[i] = neg_sums[i+1] + min(0, lst[i])

    def helper(i, s, t):
        if min_s <= s <= max_s:
            yield t
            # out.append(t)
        for j in range(i, n):
            if s + pos_sums[j] < min_s or s + neg_sums[j] > max_s:
                break
            v = lst[j]
            yield from helper(j + 1, s + v, t + (v,))
            # helper(j + 1, s + v, t + (v,))

    return helper(0, 0, ())
    # out = []
    # helper(0, 0, ())
    # return out

示例:

>>> list(subsets_of_sum([1, 2, 3, 4, 5], 3, 6))
[(1, 2),
 (1, 2, 3),
 (1, 3),
 (1, 4),
 (1, 5),
 (2, 3),
 (2, 4),
 (3,),
 (4,),
 (5,)]