【问题标题】:Is a priority queue necessary for event simulation?事件模拟是否需要优先级队列?
【发布时间】:2012-07-06 05:48:17
【问题描述】:

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

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

【问题讨论】:

    标签: sorting heap simulation priority-queue heapsort


    【解决方案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,它提供了多种事件队列实现并且是开源的(免责声明:我是开发人员之一)。

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

    一般来说,实现基于过程的离散事件模拟的便捷方式是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()));     
    }
    

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

    【讨论】:

    • 那么如果要再次处理一个事件,它本身就是newEvents的唯一项?此外,添加一个优先级低于当前正在处理的事件是否会引起任何奇怪?在某种程度上,与在同一更新周期中添加的具有更高优先级的事件相比,它似乎会获得额外的处理。
    • 是的,任何由处理currentEvent 引起的事件都将在newEvents 中,这可能包括事件本身(尽管这很不寻常,因为它有点打破@ 的隐喻987654332@,即只是发生[一次]并可能导致新事件的事情)。如果在您的应用程序中定期重新插入事件,您还可以检索(但不是出列)当前事件,然后以新的优先级requeue 它。一些专门为模拟构建的事件队列提供此操作。这可能比排队新事件便宜得多。
    • 总之,当心过早的优化:在你让事件调度过于复杂之前,使用分析器看看这是否真的是一个性能瓶颈。关于添加较低优先级事件的“怪异”,这在很大程度上取决于您的应用程序。我看到的一个危险是,您可能会“卡”在某个优先级(时间)上,甚至会及时倒退(这再次打破了隐喻)。相反,您可以考虑将优先级设为二维,例如。元组(时间,事件优先级),并允许在当前时间重新安排较低/较高优先级的事件。
    • 啊,是的,协程。我基本上是在尝试“伪造”协程,就像你在 Java 中的 Lua 中看到的那样。
    • 啊,我明白了。你看过stackoverflow.com/questions/2846428/…stackoverflow.com/questions/2846664/…吗?无论如何,祝你好运!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 2020-11-16
    • 1970-01-01
    相关资源
    最近更新 更多