python - Grouping values from list into buckets to keep sum of values at or below threshold
问题描述
I have a list of times in minutes that represent the time it takes to do some job, but the amount of time spent working cannot exceed 240 minutes in any given day.I would like to separate into N groups such that the sum of the items in the group does not exceed the threshold (240 in this example).
An example list could be [60, 70, 90, 120, 180, 240]
One possible solution to the above list would be:
[[240], [180, 60], [70, 90], [120]]
. Note how each of the sums in each list does not exceed the threshold of 240 while leaving some leftover is fine, but how do I know this is the most optimal solution? Optimal being defined as simultaneously minimizing leftover and the number of buckets.
I've been working on trying to do this with some for
loops in Python but it feels about as inefficient as possible. Also, due to the method of removing items from the list, the for
loop isn't iterating over all values. Below is my attempt.
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,))
All possible combinations:
[((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,))]
推荐阅读
- xamarin.forms - 播放音频应用程序崩溃。但在模拟器中工作正常
- android - 如何从可用的(HLS)切换exo播放器中的曲目格式?
- java - 热点 jvm 中的 C2 编译器如何将内存屏障转换为汇编代码
- reactjs - 如何将字符串值传递给另一个反应组件并从该组件获得一些响应?
- machine-learning - 如何使用 catboost 提高模型的准确性
- angular - Angular/RxJs - 使用 take(1) 取消订阅依赖的 http 服务?
- unit-testing - 如何在使用 Mockito 测试之前强制初始化应用程序类中的上下文
- c++ - 为什么我需要使用空命名空间来编译它?
- java - 原子整数的 compareandexchange() 与 compareandset()
- google-cloud-platform - 有没有办法将发布/订阅消息放入 Stackdriver?