【问题标题】:Schedule datetime intervals via different skipping methods通过不同的跳过方法安排日期时间间隔
【发布时间】:2014-11-10 22:59:36
【问题描述】:

我有一个日期时间间隔列表(开始时间和结束时间)。日期时间间隔有一些额外的属性(见 schedule)。 由于排序,我已经有了间隔的上升时间表。 现在我想实现一些方法来处理重叠冲突。 每次应该只运行一个任务。重叠的任务应该被跳过而不是被转移或延迟。

这些调度方法应该是这样的

  • FCFS(先到先得):应该明确
  • 优先级等属性的最大值
  • 事件的最长持续时间(应该类似于最大值)

所以假设我们有像 Event(Interval, priority): A(0-7, 2), B(5-10, 3), C(8-15,1), D(20-25,0) 这样的事件)。 更高的价值,更高的优先级。

FCFS:A、C、D - 最高优先级:B、D - 最长持续时间:A、C、D

这是我第一次使用 FCFS。我认为这是一种不好的方法 - 对于其他方法也是如此,因为我需要一种反向查找,而不仅仅是最后一个事件。

样本数据:

start1 = datetime.now()
start2 = datetime.now() + timedelta(seconds=40)
start3 = datetime.now() + timedelta(seconds=70)
start4 = datetime.now() + timedelta(seconds=200)

schedule = [{"start": start1,
             "end": start1 + timedelta(seconds=60),
             "name": "Task1",
             "attr": 2},
            {"start": start2,
             "end": start2 + timedelta(seconds=60),
             "name": "Task2",
             "attr": 3},
            {"start": start3,
             "end": start3 + timedelta(seconds=60),
             "name": "Task3",
             "attr": 1},
            {"start": start4,
             "end": start4 + timedelta(seconds=60),
             "name": "Task4",
             "attr": 0}
            ]

助手调用 DateTimeInterval。对于差异我使用了this

class DateTimeInterval(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __sub__(self, other):
        latest_start = max(self.start, other.start)
        earliest_end = min(self.end, other.end)
        return earliest_end - latest_start

FCFS 调度

# now we can order the beginnings
schedule_sorted = sorted(schedule, key=lambda x: (x["start"], x["name"]))

last = None
for job in schedule_sorted:
    interval = DateTimeInterval(job["start"], job["end"])
    # if there is a last entry
    if last:
        interval_last = DateTimeInterval(last["start"], last["end"])
        diff = interval - interval_last
        if diff.total_seconds() > 0:
            logging.warn("%s CONFLICT OVERLAPPING %s!", job["name"], diff)
        else:
            last = job
    else:
        last = job
    logging.info("%s: %s %s", job["name"], job["start"], job["end"])

我可能会破解所有方法,从而产生一些丑陋的代码。

有没有人(处理过实现这种不同的调度要求并且)知道一个很好的方法来做到这一点?

【问题讨论】:

  • 对于 FCFS,键(例如 datetime(start1))是否应该是第一个最旧的? attr 值是整数吗?时长是通过 schedule[key]["end"] 和 schedule[key]["start"] 的差来计算的吗?
  • 是的,FCFS 首次启动将获胜。 Attr 很可能是一个整数。持续时间由差值计算。并且计算重叠是在 DateTimeInterval 的 __sub__ 方法中实现的,例如 this
  • 我建议你开始研究 python 的 min 和 max 函数。使用让您通过自定义函数决定排序标准的关键参数。您可以在该客户功能中根据需要比较字典元素。在此处查看示例stackoverflow.com/questions/26833373/…
  • 还在调度字典中添加一些实际值以及每种情况下的预期输出。这将有助于解决问题并提供解决方案。
  • @user3885927 做到了。稍微简化一下并纠正我在编辑此帖子时所做的一些错误。我不知道这是否清楚。只有一个资源和一个作业可以一次运行。

标签: python datetime scheduling intervals


【解决方案1】:

鉴于您对其他方法的解释,我认为您应该改变您的方法。您应该首先按解决标准(FCFS、最长持续时间或优先级)对事件进行排序,而不是按开始时间排序。然后,您应该有一个方法schedule_event(),它可以将一个事件插入到已安排事件的排序列表中,或者删除这些事件。

我们所做的初始排序可确保首先安排所有优先级较高的事件,然后在发生冲突时删除优先级较低的事件。这里有一些骨架代码:

def sort_events(method_name, events):
  ######
  return sorted_events

def schedule_event(event, scheduled_events):
  if not len(scheduled_events):
    scheduled_events.append(event)
    return
  right_index = bisect.bisect(scheduled_events, event)
  left_index = right_index - 1
  if len(scheduled_events) == right_index:
    if event.conflicts(scheduled_events[left_index]):
      return
  else:
    if event.conflicts(scheduled_events[left_index]) or event.conflicts(scheduled_events[right_index]):
      return
  bisect.insort(scheduled_events, event)

def main():
  scheduled_events = []
  sorted_events = sort_events("FCFS", events)
  for event in sorted_events:
    schedule_event(event, scheduled_events)

您应该按排序顺序维护scheduled_events 列表中的事件。按开始时间顺序维护它们。您可以使用bisect module 找出插入时是否有任何冲突。使用bisect.bisect() 查找插入点并检查紧邻左侧和紧邻右侧的冲突。在左边,结束时间应该早于当前事件的开始时间。在右边,开始时间应该在当前事件的结束时间之后。如果没有冲突,使用bisect.insort() 插入,否则,什么也不做。

另外,为了使排序成为可能,您需要定义自己的 Event 类而不是使用字典,并在该类中实现 __lt__() 方法。

class Event:
  def __init__(self, start_time, end_time):
    self.start_time = start_time
    self.end_time = end_time

  def __lt__(self,):
    pass

【讨论】:

  • 是的,它不仅仅是一个单环。 FCFS 只是第一次获胜。如果持续时间应该获胜,则总体冲突间隔中的任务以最长的持续时间获胜。如果那么其他人也可以运行这些也应该再次添加。希望它会更容易:我想为天线安排卫星通行证。
  • 添加了一个例子,希望对您有所帮助。仅 B,单个事件计数,而不是整体持续时间。
  • 感谢您的提示,我认为不同的排序是最简单的方法。我对 schedule_event 的一个好方法很感兴趣......我如何编写插入或不插入的标准?这就是我的问题。
  • 是的,您应该使用bisect.bisect() 来查找插入点并检查左右两边的冲突。在左边,结束时间应该早于当前事件的开始时间。在右边,开始时间应该在当前事件的结束时间之后。如果没有冲突,使用bisect.insort() 插入,否则,什么也不做。
  • 好的,但是我应该如何评估重叠? bisect 只是比较项目,没有日期时间间隔......抱歉已经为此花费了几个小时。我不是只见树木不见森林。
【解决方案2】:

这应该可以帮助您开始,根据需要调整您的 lambda 函数。我刚刚向您展示了如何做 min 但 max 完全相似。 min 的文档是here

ByFCFS = min(schedule,key=lambda k:k["start"])
ByAttr= min(schedule,key=lambda k:k["attr"])
ByTimeDiff = min(schedule,key=lambda k:k["end"]-k["start"])

>>> ByFCFS
{'start': datetime.datetime(2014, 11, 10, 20, 32, 54, 151000), 'end': datetime.datetime(2014, 11, 10, 20, 33, 54, 151000), 'name': 'Task1', 'attr': 2}
>>> ByAttr
{'start': datetime.datetime(2014, 11, 10, 20, 36, 14, 152000), 'end': datetime.datetime(2014, 11, 10, 20, 37, 14, 152000), 'name': 'Task4', 'attr': 0}
>>> ByTimeDiff
{'start': datetime.datetime(2014, 11, 10, 20, 32, 54, 151000), 'end': datetime.datetime(2014, 11, 10, 20, 33, 54, 151000), 'name': 'Task1', 'attr': 2}
>>> 

请注意,如果有多个最小值(如 ByTimeDiff 示例),这将返回第一条记录。如果需要做max,把min换成max。

【讨论】:

  • 感谢您的回复。可能不清楚我想要什么。时间就是尺度。我不想只是对它进行排序。我希望每次都按时间排序。只是我想用合适的启发式解决的冲突。最小,最大功能对我来说很清楚。这是一种通过查找来安排将来忽略什么或使用什么的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多