2010-05-17 93 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