【问题标题】:Most elegant way to calculate completion times for a list of jobs in Python在 Python 中计算作业列表完成时间的最优雅方法
【发布时间】:2016-10-05 09:58:43
【问题描述】:

我有一个[(weight, length)] 形式的工作列表,例如

[(99, 1), (100, 3), (100, 3), (99, 2), (99, 2)]

但要大得多。

我已经编写了一个函数,它根据我作为参数传递的不同键来调度它们。这意味着对于每个作业,我将其完成时间计算为所有先前作业的总和。最终目标是计算加权完成时间:weight[i] * completion_time[i]

目前,如果不将所有长度分隔在一个单独的列表中,我看不到一种优雅的方法,这对我来说似乎不是很 Pythonic。

这里是代码

def schedule(jobs_list, sort_key):
     sorted_jobs = sorted(jobs_list, key=sort_key, reverse=True)
     lengths = [job[1] for job in sorted_jobs]
     weighted_completion_times = [sum(lengths[:i + 1]) * sorted_jobs[i][0] for i in range(len(sorted_jobs))]
     return sum(weighted_completion_times)

这里是示例用法:

schedule(jobs, lambda t: (t[0] - t[1], t[0]))

理想情况下,我希望解决方案既可读又高效(即不创建另一个长度列表)

【问题讨论】:

  • ... for i in range(len(...)) ... 通常是 Python 中的一种代码异味,其中首选的习惯用法是直接迭代一个值序列,而不是迭代索引然后再索引该序列。

标签: python list python-3.x list-comprehension


【解决方案1】:

您想使用itertools.accumulate() iterable 来生成您的长度的累积重量:

from itertools import accumulate

def schedule(jobs_list, sort_key):
     sorted_jobs = sorted(jobs_list, key=sort_key, reverse=True)
     acc_lengths = accumulate(job[1] for job in sorted_jobs)
     weighted_completion_times = (al * job[0] for al, job in zip(acc_lengths, sorted_jobs))
     return sum(weighted_completion_times)

请注意,这绝不会构建除排序列表之外的新列表。通过避免构建中间列表以及避免重新汇总越来越长的子列表(使这种 O(N) 与您的 O(N^2) 方法相比),上述方法也更有效;仅在您的短样本上,时间就有 25% 的改进:

>>> from itertools import accumulate
>>> from timeit import timeit
>>> def schedule_lists(jobs_list, sort_key):
...      sorted_jobs = sorted(jobs_list, key=sort_key, reverse=True)
...      lengths = [job[1] for job in sorted_jobs]
...      weighted_completion_times = [sum(lengths[:i + 1]) * sorted_jobs[i][0] for i in range(len(sorted_jobs))]
...      return sum(weighted_completion_times)
...
>>> def schedule_acc(jobs_list, sort_key):
...      sorted_jobs = sorted(jobs_list, key=sort_key, reverse=True)
...      acc_lengths = accumulate(job[1] for job in sorted_jobs)
...      weighted_completion_times = (al * job[0] for al, job in zip(acc_lengths, sorted_jobs))
...      return sum(weighted_completion_times)
...
>>> jobs = [(99, 1), (100, 3), (100, 3), (99, 2), (99, 2)]
>>> timeit('schedule(jobs, lambda t: (t[0] - t[1], t[0]))',
...        'from __main__ import jobs, schedule_lists as schedule',
...         number=100000)
0.6098654230008833
>>> timeit('schedule(jobs, lambda t: (t[0] - t[1], t[0]))',
           'from __main__ import jobs, schedule_acc as schedule',
...        number=100000)
0.4608557689934969

但是,当您将作业列表大小增加到 1000 时,差异会更加明显:

>>> import random
>>> jobs = [(random.randrange(80, 150), random.randrange(1, 10)) for _ in range(1000)]
>>> timeit('schedule(jobs, lambda t: (t[0] - t[1], t[0]))',
...        'from __main__ import jobs, schedule_lists as schedule',
...         number=1000)
5.421368871000595
>>> timeit('schedule(jobs, lambda t: (t[0] - t[1], t[0]))',
...        'from __main__ import jobs, schedule_acc as schedule',
...         number=1000)
0.7538741750176996

【讨论】:

  • 哇!事实上,在 20000 个长列表中,您的解决方案速度快了近 10 倍!
猜你喜欢
  • 1970-01-01
  • 2020-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多