[英]Grouping values from list into buckets to keep sum of values at or below threshold
我有一个以分钟为单位的时间列表,表示完成某项工作所需的时间,但在任何一天中,工作所花费的时间不能超过 240 分钟。我想分成 N 组,以便项目的总和在组中不超过阈值(本例中为 240)。
示例列表可以是[60, 70, 90, 120, 180, 240]
上述列表的一种可能解决方案是: [[240], [180, 60], [70, 90], [120]]
。 请注意每个列表中的每个总和如何不超过 240 的阈值,同时留下一些剩余部分很好,但我怎么知道这是最佳解决方案? 最优被定义为同时最小化剩余和桶的数量。
我一直在努力尝试用 Python 中的一些for
循环来做到这一点,但它感觉效率尽可能低。 此外,由于从列表中删除项目的方法, for
循环不会遍历所有值。 下面是我的尝试。
import random
import math
def roundup(x):
return int(math.ceil(x / 10.0)) * 10
threshold = 240;
randomlist = []
for i in range(0,10):
n = random.randint(30,240)
randomlist.append(roundup(n))
days = []
print (randomlist)
for item in randomlist:
schedule = []
schedule.append(item)
minutes_remaining = threshold - item
for item2 in randomlist:
if randomlist.index(item) != randomlist.index(item2):
if item2 <= minutes_remaining:
minutes_remaining -= item2
schedule.append(item2)
randomlist.remove(item2)
randomlist.remove(item)
days.append(schedule)
print (days)
from itertools import permutations, product, accumulate
THRESHOLD = 240
def make_groups(lst, n):
"""
Make n groups. The sum of the items in the group does not exceed
the threshold.
"""
groups = []
grps_range = [range(1, len(lst) - n + 2) for _ in range(n)]
combo = [i for i in product(*grps_range) if sum(i) == len(lst)]
for c in combo:
marker = [0] + list(accumulate(c))
group = [tuple(sorted(lst[i:j])) for i, j in zip(marker, marker[1:])]
conform = [sum(g) <= THRESHOLD for g in group]
if all(conform):
groups.append(tuple(sorted(group)))
return groups
def ngroups_leftover(entry):
"""
Returns number of groups and sum of left over times
"""
ngroups = len(entry)
leftover = [THRESHOLD - sum(x) for x in entry]
return ngroups, sum(leftover)
groups = []
l = [60, 70, 90, 120, 180, 240]
perm = permutations(l)
for p in perm:
for i in range(1, len(p) + 1):
result = make_groups(p, i)
groups += result
unique_groups = set(groups)
x = sorted(unique_groups, key=ngroups_leftover)
# There are many solutions which result in the same number of groups
# and left over time. But, the first entry would do.
print(x[0])
Output
((60, 180), (70,), (90, 120), (240,))
所有可能的组合:
[((60, 180), (70,), (90, 120), (240,)),
((60, 70), (90, 120), (180,), (240,)),
((60, 180), (70, 120), (90,), (240,)),
((60, 90), (70, 120), (180,), (240,)),
((60, 120), (70, 90), (180,), (240,)),
((60, 70, 90), (120,), (180,), (240,)),
((60, 180), (70, 90), (120,), (240,)),
((60, 180), (70,), (90,), (120,), (240,)),
((60,), (70, 90), (120,), (180,), (240,)),
((60, 70), (90,), (120,), (180,), (240,)),
((60,), (70, 120), (90,), (180,), (240,)),
((60, 120), (70,), (90,), (180,), (240,)),
((60, 90), (70,), (120,), (180,), (240,)),
((60,), (70,), (90, 120), (180,), (240,)),
((60,), (70,), (90,), (120,), (180,), (240,))]
from itertools import permutations
l =[60, 70, 90, 120, 180, 240]
perm = permutations(l)
results = []
for p in perm:
for i in range(1, len(p)):
if sum(p[ : i]) <= 240:
results += [ tuple( sorted( p[ : i] ) ) ]
print(list(set(results)))
打印输出是:
[(90,), (90, 120), (60, 70, 90), (180,), (60,), (120,), (60, 180), (240,), (70,), (70, 120), (60, 90), (70, 90), (60, 70), (60, 120)]
我尝试了一个贪婪的解决方案O( N log(N) + N)
,不确定它是否是最优的。
inp = [60, 70, 90, 120, 180, 240]
inp.sort()
i =0
j = len(inp)-1
k = 240
while j >= i:
tmp = []
tmp.append(inp[j])
sm = inp[j]
while i < j:
if sm + inp[i] > k:
break
sm += inp[i]
tmp.append(inp[i])
i+=1
print(tmp)
j-=1
Output:
[240]
[180, 60]
[120, 70]
[90]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.