现在,在我的基本事件模拟引擎中,我只是简单地使用事件对象列表来按模拟的每个步骤更新它们的优先级。我这样做是因为可以在事件更新期间创建新事件并将其附加到列表中,当事件过期时,我只需将其“交换并弹出”列表中的最后一个事件以获得性能。我应该只使用两个优先级队列吗?看起来排序每个步骤的n log n至少是相同的,如果不是比所有事件(n log n?)的成本更低,将每个未到期的事件放在另一个列表中,该列表构建在优先级队列中以用于下一个更新步骤
编辑:我认为将“事件”称为“流程”更为贴切,而将整个事件称为更多的流程调度模拟。队列中的每个对象都按优先级顺序更新状态,然后只有当它已经过期(输入某种结论状态)才会被丢弃而不会重新插入队列。这就是如何只有一个优先级队列可能是一个问题;当一个对象被重新插入时,它仍然具有最低优先级,并且将被再次拉出。我正在考虑使用第二个队列来插入所有新生成的进程对象和未到期的进程对象,而不考虑优先级,然后我可以构建堆并在下一个更新周期开始之前将其与活动队列交换。
答案 0 :(得分:1)
通常,您应该在(顺序)离散事件模拟器中使用一个事件队列。我不太清楚你的意思是什么?过期'事件,但如果因为它们不再有效而忽略这些事件,一个简单的解决方案就是丢弃它们而不是处理它们,例如看到这个伪Java代码:
while (!terminationConditionMet() && !eventQueue.isEmpty()) {
Event currentEvent = eventQueue.getNextEvent();
if(currentEvent.isExpired())
continue;
List<Event> newEvents = currentEvent.process();
eventQueue.addEvents(newEvents);
}
通常(对于足够大的事件集),这应该比在每个步骤之后重新排序事件列表快得多。
BTW许多事件队列实现在O(1)中实现了出列,即使对于某些操作,它们的最坏情况性能可能不比排序好,但实际的实现工作要好得多(即它们具有较小的常数/更好的平均性能)。如果您正在使用Java,您可能需要查看JAMES II,它提供了几个事件队列实现并且是开源的(免责声明:我是开发者之一)。
编辑(解决重新制定的问题):
通常,实现基于流程的离散事件模拟的便捷方式是coroutines,有关详细信息,请参阅示例this report。但是,如果您想坚持您的实现,您仍然可以将优先级更改为元组(timeStep,processPriority)
,并使用上述伪代码的改编版本,如下所示:
while (!terminationConditionMet() && !queue.isEmpty()) {
//Read out event
Tuple minTime = queue.getMinTime();
ProcessEvent currentEvent = queue.dequeue();
//Execute event
List<ProcessEvent> newlySpawnedProcesses = processEvent.process();
//Re- and enqueue new events
int nextTimeStep = minTime.getTime() + 1;
if(!currentEvent.finalStateReached())
queue.requeue(currentEvent, new Tuple(nextTimeStep, currentEvent.getPriority()));
for(ProcessEvent event : newlySpawnedProcesses)
queue.enqueue(event, new Tuple(nextTimeStep, event.getPriority()));
}
当然,这假设您正在使用足够通用的事件队列实现,以允许您指定自己的时间/优先级顺序,例如,通过指定自定义比较器。