我目前面临一个困难的排序问题。我有一组需要相互排序的事件(comparison sort)和它们在列表中的相对位置。
在最简单的术语中,我有事件列表,每个事件都有一个优先级(整数),一个持续时间(秒),以及事件可以出现在列表中的最早发生时间。我需要根据优先级对事件进行排序,但是在最早发生的时间之前,列表中不会出现任何事件。这是一个(希望)让它更清晰的例子:
// Psuedo C# code
class Event { int priority; double duration; double earliestTime ; }
void Example()
{
Event a = new Event { priority = 1, duration = 4.0, earliestTime = 0.0 };
Event b = new Event { priority = 2, duration = 5.0, earliestTime = 6.0 };
Event c = new Event { priority = 3, duration = 3.0, earliestTime = 0.0 };
Event d = new Event { priority = 4, duration = 2.0, earliestTime = 0.0 };
// assume list starts at 0.0 seconds
List<Event> results = Sort( new List<Event> { a, b, c, d } );
assert( results[ 0 ] == a ); // 4.0 seconds elapsed
assert( results[ 1 ] == c ); // 7.0 seconds elapsed
assert( results[ 2 ] == b ); // 12.0 seconds elapsed
assert( results[ 3 ] == d ); // 14.0 seconds elapsed
}
项目“b”必须是最后的,因为它不允许在列表中的6.0秒之前开始,所以它被推迟并且“c”在“b”之前开始,即使它的优先级较低。 (希望上面解释我的问题,如果不让我知道,我会编辑它。)
我目前的想法是使用insertion sort来管理排序过程。与许多其他常见的排序算法不同,插入排序一次一个地按顺序决定列表的顺序。因此,对于每个索引,我应该能够找到满足最早发生时间的下一个最低优先级事件。
我希望找到有关排序算法和数据结构的资源,以帮助我为这种“排序”问题设计一个很好的解决方案。我真正的问题实际上比这更复杂:分层排序,事件之间的变量缓冲,多个非恒定时间约束,因此越多的信息或想法越好。速度和空间并不是真正的问题。排序的准确性和代码的可维护性是一个问题。
修改:澄清(基于评论)
修改:回答
虽然David Nehme给出了我选择的答案,但我想指出他的答案是插入的内心,其他几个人提供了插入排序类型的答案。这向我证实了专门的插入排序可能是要走的路。感谢大家的回答。
答案 0 :(得分:10)
这实际上不仅仅是一个排序问题。这是发布日期的单机调度问题。根据您的尝试,问题可能是NP-Hard。例如,如果您试图最小化完成时间的加权和(权重与优先级成反比),问题是categorized为
1|ri;pmtn|Σ wiCi
并且是NP难的。这个主题有很多papers,但它可能超出了你的需要。
在您的情况下,您永远不需要具有间隙的解决方案,因此您可能只需要进行简单的离散事件模拟(O(n log(n)))时间。您需要将released_jobs存储为优先级队列。
unreleased_jobs = jobs // sorted list of jobs, by release date
released_jobs = {} // priority queue of jobs, by priority
scheduled_jobs = {} // simple list
while (!unreleased_jobs.empty() || !released_jobs.empty()) {
while (unreleased_jobs.top().earliestTime <= t) {
released_jobs.push(unreleased_jobs.pop())
}
if (!released_jobs.empty()) {
next_job = released_jobs.pop();
scheduled_jobs.push_back(next_job)
t = t + next_job.duration
} else {
// we have a gap
t = unreleased_jobs.top().earliestTime
}
}
一个问题是,您可能会在短暂的高优先级作业之前拥有一个发布时间较低的优先级作业,但它会生成一个没有间隙的属性计划(如果计划没有间隙)是可能的。
答案 1 :(得分:2)
换句话说,您希望在制定两个约束时强化整体运行时间(强:最早执行点,弱:优先级)?这称为constraint satisfaction problem。这种问题有特殊的解决方法。
顺便说一下,jakber的解决方案不起作用。即使没有持续时间,以下示例显然也会失败:
event a (priority = 1, start = 5)
event b (priority = 2, start = 0)
排序后的序列为a
,b
,而想要的结果肯定是b
,a
。
答案 2 :(得分:2)
我想:
将时间线转换为任务列表,并等待(为间隙)。
问题:
编辑:
我的问题的答案是:
在这种情况下,算法可能是:
该算法也是O(n ^ 2)。
答案 3 :(得分:0)
我认为您应该对列表进行两次排序:首先按优先级排序,然后按最早时间排序,使用任何稳定排序算法,例如插入排序。这样,时间会增加,每次事情都会按优先顺序排序。
除非您看到我不知道的内容,否则您可以完全忽略每个事件的持续时间以进行排序。
答案 4 :(得分:0)
听起来你确实想要一个基于比较的排序。您的排序键是{earliestTime,priority},按此顺序排列。由于您的示例是伪C#,我将为您提供伪C#解决方案:
class Event : IComparable<Event>, IComparable{
int priority;
double duration;
double earliestTime;
public int CompareTo(Event other){
if(other == null)
return 1; /* define: non-null > null */
int cmp = earliestTime.CompareTo(other.earliestTime);
if(cmp != 0)
return cmp;
/* earliestTimes were equal, so move on to next comparison */
return priority.CompareTo(other.priority);
}
int IComparable.CompareTo(object other){ /* for compatibility with non-generic collections */
if(other == null)
return 1; /* define: non-null > null */
Event e_other = other as Event;
if(e_other == null) /* must have been some other type */
throw new ArgumentException("Must be an Event", "other");
return CompareTo(e_other); /* forward to strongly-typed implementation */
}
}
现在,您的列表将按照您的断言所期望的那样排序。
修改强>:
我最初的假设是,事件将被从列表中挑选出来并传递给一个单独的线程,以便队列管理器可以按时触发下一个事件,但是根据我收到的评论,我得知这可能是一个单线程的方法,但仍允许更高优先级的事件尽可能接近其开始时间,这是更理想的。在这种情况下,CompareTo
函数应更改如下:
public int CompareTo(Event other){
if(other == null)
return 1; /* define: non-null > null */
int cmp = priority.CompareTo(other.priority);
if(cmp == 0)
/*
* calculate and compare the time each event will be late
* if the other one were to start first. This time may be
* negative if starting one will not make the other one late
*/
return (earliestTime + duration - other.earliestTime).CompareTo(
other.earliestTime + other.duration - earliestTime);
/*
* they're different priorities. if the lower-priority event
* (presume that greater priority index means lower priority,
* e.g. priority 4 is "lower" priority than priority 1), would
* would make the higher-priority event late, then order the
* higher-priority one first. Otherwise, just order them by
* earliestTime.
*/
if(cmp < 0){/* this one is higher priority */
if(earliestTime <= other.earliestTime)
/* this one must start first */
return -1;
if(other.earliestTime + other.duration <= earliestTime)
/* the lower-priority event would not make this one late */
return 1;
return -1;
}
/* this one is lower priority */
if(other.earliestTime <= earliestTime)
/* the other one must start first */
return 1;
if(earliestTime + duration <= other.earliestTime)
/* this event will not make the higher-priority one late */
return -1;
return 1;
}
针对任何假设进行测试,但我认为这是我们正在寻找的。 p>
答案 5 :(得分:0)
如果您拥有一组有限的优先级,则可以保留一组时间排序列表,每个级别为1。每当您需要下一个事件时,请按优先级顺序检查每个列表的头部,直到找到其开始时间已过去的列表。 (在检查时跟踪最短的开始时间 - 如果尚未准备好任何事件,您知道要等待哪一个)
答案 6 :(得分:0)
听起来像是我前几天遇到的一个问题,答案是here 假设你正在使用C#...
答案 7 :(得分:0)
顺便提一下,在最一般的情况下,可能没有解决方案(除非允许空白,正如道格拉斯指出的那样)。例如:
Event a = new Event { priority = 1, duration = 1.0, earliestTime = 4.0 };
Event b = new Event { priority = 2, duration = 1.0, earliestTime = 4.0 };
Event c = new Event { priority = 3, duration = 1.0, earliestTime = 4.0 };
Event d = new Event { priority = 4, duration = 1.0, earliestTime = 4.0 };
答案 8 :(得分:0)
我不完全确定我理解你问题的复杂性,但我的直觉告诉我你需要定义优先级和开始时间之间的关系。例子是:
Event a = new Event { priority = 1, duration = 4.0, earliestTime = 1.0 };
Event b = new Event { priority = 2, duration = 5.0, earliestTime = 0.0 };
那么,我们是否继续在时间= 0开始b
,或者我们等待勾选然后开始a
因为它的优先级更高?假设有更多事件具有更多优先级和更长时间的权衡。我认为您需要一条规则:“如果下一个事件是X更高优先级并且间隙(现在和最早时间之间)小于Y秒,则等待然后启动更高优先级事件。否则启动低优先级事件(因此推回高优先级)“。
答案 9 :(得分:0)
以下是道格拉斯答案中的一些Python代码。首先我们按优先顺序排序,然后我们以选择排序方式拟合时间轴:
#!/usr/bin/env python
MIN_PRIORITY = 100
class Event(object):
def __init__(self, name, priority, duration, earliestTime):
self.name = name
self.priority = priority
self.duration = duration
self.earliestTime = earliestTime
def __str__(self):
return "%-10s: P %3d D %3.1f T %3.1f" % (self.name, self.priority, self.duration, self.earliestTime)
def sortEvents(_events):
def comparePriority(event1, event2):
if event1.priority < event2.priority: return -1
if event1.priority > event2.priority: return 1
return 0
# Get a copy of the events and sort by priority
events = [e for e in _events]
events.sort(cmp=comparePriority)
# Select one event at a time, checking for compatibility with elapsed time
elapsedTime = 0.0
sortedEvents = []
while events:
minGap = events[0].earliestTime - elapsedTime
for e in events:
currentGap = e.earliestTime - elapsedTime
if currentGap < minGap:
minGap = currentGap
if currentGap <= 0.0:
sortedEvents.append(e)
elapsedTime += e.duration
events.remove(e)
break
# If none of the events fits, add a suitable gap
if minGap > 0:
sortedEvents.append( Event("gap", MIN_PRIORITY, minGap, elapsedTime) )
elapsedTime += minGap
return sortedEvents
if __name__ == "__main__":
#e1 = Event("event1", 1, 1.0, 4.0)
#e2 = Event("event2", 2, 1.0, 6.0)
#e3 = Event("event3", 3, 1.0, 8.0)
#e4 = Event("event4", 4, 1.0, 10.0)
e1 = Event("event1", 1, 4.0, 0.0)
e2 = Event("event2", 2, 5.0, 6.0)
e3 = Event("event3", 3, 3.0, 0.0)
e4 = Event("event4", 4, 2.0, 0.0)
events = [e1, e2, e3, e4]
print "Before:"
for event in events: print event
sortedEvents = sortEvents(events)
print "\nAfter:"
for event in sortedEvents: print event