我想定义一个带有以下参数的函数: -数字列表 -最小 -最大
它应返回列表(或集合)的列表,其中每个列表的总和介于最小值和最大值之间。列表中不应有重复项。
我所得到的不会返回所有可能的列表:
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]]
答案 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
这就是我想出的结果
测试输入为
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,并进行了一些优化。首先对列表进行排序,然后计算每个索引之后的正数和负数之和;这些可以用作剩余元素求和的界限。对于包含负数的列表,这将给出正确的答案。作为对列表进行排序的副作用,这些子集按字典顺序查找。
通常,生成器函数对于组合问题比返回列表中的所有结果更有用,因为可能有很多解决方案,并且您通常不需要一次将所有结果都存储在内存中。但是,yield
比append
慢一点,因此,如果您始终希望结果显示在列表中,那么我已经显示了使用注释进行适当的更改。
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,)]