3

现在在我的基本事件模拟引擎中,我只是简单地使用事件对象列表来根据模拟的每一步的优先级进行更新。我这样做是因为可以在事件更新期间创建新事件并将其附加到列表中,当事件到期时,我只需将其与列表中的最后一个事件“交换并弹出”以提高性能。我应该只使用两个优先级队列吗?似乎对每一步排序的 n log n 至少是相同的,如果不比将所有事件(n log n?)出队的成本低的话,将每个未过期的事件放入另一个列表中,该列表内置到下一个更新步骤的优先级队列中.

编辑:我认为更倾向于将“事件”称为“进程”,然后将整个事情称为进程调度模拟。队列中的每个对象都按优先级顺序更新其状态,然后只有当它过期(进入某种结束状态)时,它才会被丢弃并且不会重新插入队列中。这就是只有一个优先级队列可能是个问题的原因。当一个对象被重新插入时,它仍然具有最低的优先级,并且会再次被拉出。我正在考虑使用第二个队列来插入所有新生成的进程对象和未过期的进程对象,而不考虑优先级,然后我可以构建堆并在下一个更新周期开始之前将其与活动队列交换。

4

1 回答 1

1

通常,您应该在(顺序)离散事件模拟器中使用一个事件队列。我不太清楚你对“过期”事件的意思,但如果这些事件因为它们不再有效而被忽略,一个简单的解决方案是丢弃它们而不是处理它们,例如看这个伪 Java 代码:

 while (!terminationConditionMet() && !eventQueue.isEmpty()) {
   Event currentEvent = eventQueue.getNextEvent();
   if(currentEvent.isExpired())
     continue;
   List<Event> newEvents = currentEvent.process();
   eventQueue.addEvents(newEvents); 
 }

通常(对于足够大的事件集),这应该比在每一步之后重新排序事件列表要快得多。

顺便说一句,许多事件队列实现在 O(1) 中实现了出队,即使对于某些操作,它们最坏情况下的性能可能并不比排序好,但实际实现工作得更好(即它们具有更小的常量/更好的平均性能)。如果您正在使用 Java,您可能想查看JAMES II,它提供了多种事件队列实现并且是开源的(免责声明:我是开发人员之一)。

编辑(解决重新制定的问题)

通常,实现基于流程的离散事件模拟的一种便捷方式是协程,例如,请参阅此报告了解详细信息。但是,如果您想坚持自己的实现,您仍然可以将优先级更改为元组(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()));     
}

当然,这假设您正在使用足够通用的事件队列实现,以允许您指定自己的时间/优先级顺序,例如通过指定自定义比较器。

于 2012-07-07T13:59:34.193 回答