2010-05-17 46 views
1

我希望能帮助调试multiset容器的一些奇怪行为。偶尔,容器似乎停止排序。这是一个偶然的错误,在很长一段时间之后只有一些模拟中显而易见,并且我对想法不甚了解。 (我是一个业余程序员 - 各类建议,欢迎。)Multiset容器似乎停止排序

我的容器是std::multiset持有Event结构:

typedef std::multiset< Event, std::less<Event> > EventPQ; 

与他们double time成员排序的Event结构:

struct Event { 

public: 
explicit Event(double t) : time(t), eventID(), hostID(), s() {} 
Event(double t, int eid, int hid, int stype) : time(t), eventID(eid), hostID(hid), s(stype) {} 

    bool operator < (const Event & rhs) const { 
    return (time < rhs.time); 
    } 

    double time; 
    ... 
}; 

程序循环遍历无序次数的事件到EventPQ currentEvents,然后按顺序拉取事件。很少,在添加了一些事件(完全“合法”的时间)后,事件开始无序执行。

什么使得事件无法正确排序? (或者什么可以搞乱迭代器?)我已经检查过所有添加的事件时间都是合法的(即,都超过了当前的模拟时间),并且我也确认了错误不会发生,因为两个事件碰巧得到预定在同一时间。

我很乐意就如何解决这个问题提出建议。

执行和添加事件的代码如下为好奇:

double t = 0.0; 
    double nextTimeStep = t + EPID_DELTA_T; 
    EventPQ::iterator eventIter = currentEvents.begin(); 

while (t < EPID_SIM_LENGTH) { 

    // Add some events to currentEvents 

    while ((*eventIter).time < nextTimeStep) { 

     Event thisEvent = *eventIter; 
    t = thisEvent.time; 
    executeEvent(thisEvent); 
    eventCtr++; 
    currentEvents.erase(eventIter); 
    eventIter = currentEvents.begin(); 

    } 

    t = nextTimeStep; 
    nextTimeStep += EPID_DELTA_T; 
} 


void Simulation::addEvent(double et, int eid, int hid, int s) { 
    assert(currentEvents.find(Event(et)) == currentEvents.end()); 

    Event thisEvent(et, eid, hid, s); 
    currentEvents.insert(thisEvent); 
} 

我要补充一点偶然的事件,在执行时,将删除currentEvents其他事件。这与

double oldRecTime = 10.0; // gets defined legitimately in simulation 
EventPQ::iterator epqItr = currentEvents.find(Event(oldRecTime)); 
assert(currentEvents.count(Event(oldRecTime)) == 1); 
currentEvents.erase(epqItr); 

做即使这个代码看起来还好,我想知道其他的方式我可以检查这是怎么回事 - 我目前使用了大量的断言()和cout < <检查。

+0

为什么'oldRecTime'没有分配任何值? – AnT 2010-05-17 19:17:26

+0

为什么使用multiset当你走出自己的方式,以防止重复被添加? – 2010-05-17 19:22:33

+0

@Andrey:我不知道如何综合这里的问题代码。我在模拟中搜索之前定义了oldRecTime。 (具体来说就是先前计划从感染中恢复的时间,并且它存储在Host类中。当发生感染事件时,我将重新计算先前计划的所有恢复时间,从currentEvents中删除它们的相应事件并添加更新的事件。) – Sarah 2010-05-17 19:26:40

回答

0

在模拟中,在那里我有评论

// Add some events to currentEvents 

事件被添加到进行中...。 (希望很清楚。)如果添加的事件发生在队列顶部,我认为它会颠倒指向currentEvents.begin()的迭代器。我在内部while循环之前立即重置迭代器,并且事情似乎可行。

我会更新这个问题,如果这不是解决方案,或者如果我在这里有什么其他问题。

感谢所有评论;它帮助我了解我应该如何解决这些问题。

1

您的事件处理周期无法检查队列是否为空。否则,一切看起来都很好(或多或少)。

但是,如果您的currentEvents队列中的事件用完,则行为未定义。它可能会表现为事件被无序处理的事物。实际上,我看到关联容器的一些实现用“虚拟循环”数据结构来表示它们,从某种意义上说,如果您忽略受控序列的结尾并继续迭代,则迭代器将出现在序列。你的情况会发生吗?

与您的代码立即产生的另一个问题:如果新事件到达队列中的值小于“当前”时间的time,会发生什么?我没有看到任何可以在代码中捕获这种情况的检查。很明显,如果发生这种情况,即如果某些事件“太迟”到达,无论您如何实施它们,它们都可能轻易被无序处理。

+0

为简洁起见,我没有在这里包括它们,但是我确实声明了currentEvents.size()> 0。不幸的是这不是问题。 – Sarah 2010-05-17 19:09:45

+0

我添加了断言,以确认添加的时间确实是合法的(即大于当前时间)。这似乎也不是问题。 (感谢提及“虚拟循环”的数据结构 - 很好了解。) – Sarah 2010-05-17 19:39:52

1

如果可能的话,我建议将double改为用作某种整数类型的键。 setmultiset的密钥需要严格的弱排序 - 并且而不是(通常)满足该要求(也没有任何其他IEEE浮点类型)。

+0

谢谢。我会作弊,使整数密钥像ceil(1000.0 *时间)? (我在另一点上回答了Andrey;如果currentEvents.size == 0,模拟会中断)。我可以通过定义一些小数字epsilon来测试严格弱排序是否是问题,并断言时间必须至少小于epsilon ? – Sarah 2010-05-17 19:24:11

+0

乘以1000不会真的有帮助(你仍然留下像NaN这样的东西没有比较有意义的可能性)。定义一个Epsilon使情况变得更糟(它打破了严格的弱秩序,即使*没有像NaN这样的东西)。 – 2010-05-17 20:54:19