2009-07-14 64 views
2

我試圖在C++中實現Sieve of Eratosthene。然而,經過多次嘗試,我總是得到運行時錯誤。我認爲這與使用迭代器的狀態在某處遭到破壞有關。儘管如此,我仍然無法忍受。這裏是我的代碼:這個算法實現有什麼問題[Erathosthene的篩選]

//Sieves all multiples of the current sequence element 
    bool multiple_sieve(std::list<int>& num_list) 
    { 
     std::list<int>::iterator list_iter(num_list.begin()); 
     std::list<int>::reverse_iterator last_element_iter(num_list.rbegin()); 

     for(std::list<int>::iterator elements_iter(++list_iter); 
      elements_iter != num_list.end();) 
     { 
      if((*elements_iter % *list_iter == 0) && 
      (*elements_iter <= *last_element_iter) && (*list_iter != 1)) 
       num_list.erase(elements_iter); 
      else ++elements_iter; 
     } 
     return true; 
    } 

    std::list<int>& prime_sieve(std::list<int>& num_list) 
    { 
     for(std::list<int>::iterator list_iter(num_list.begin()); 
      list_iter != num_list.end(); ++list_iter) 
      multiple_sieve(num_list); 
     return num_list; 
    } 

我在做什麼錯了?什麼是運行時錯誤?

更新:當我在我的測試中運行這個時,我得到一個消息「列表迭代器不兼容」的錯誤。

+0

你會得到什麼錯誤? – n8wrl 2009-07-14 19:59:01

回答

5

這條線:

num_list.erase(elements_iter); 

將會給你帶來問題,因爲要修改的列表,而迭代它。你可以這樣做是爲了避免這樣的問題:

elements_iter = num_list.erase(elements_iter); 

ETA:刪除的東西約擦除()無效其他迭代器(看起來他們是在這種情況下是安全的) - 只是elements_iter設置爲從擦除的返回值()和你應該很好走。

+2

這可能很有趣,儘管它可能不會改變列表中的元素亂七八糟的事實:「列表具有插入和拼接不會使迭代器無法列出元素的重要屬性,即使刪除也只會使迭代器指向被刪除的元素。「從http://www.sgi.com/tech/stl/List.html – Jonas 2009-07-14 20:09:32

+0

@Jonas - 是的,我正在查看其他迭代器,並假設其中一個可能指向與elements_iter相同的元素。但是,看起來像last_element_iter是安全的,因爲它不應該指向與elements_iter相同的元素。它也看起來像list_iter也是安全的,但我不是100%確定的。 – 2009-07-14 20:39:50

2
num_list.erase(elements_iter) 

我不認爲你應該這樣做,這會從你的列表中完全刪除數字,你寧願要將它標記爲非素數。

如果你真的想用STL代替布爾數組,最好使用std :: bitset,就像this implementation(好吧,也許還有更好的)。

+0

列表動態調整刪除序列中的元素,因此迭代器應該仍然有效。告訴我如何將序號標記爲非素數。 – gogole 2009-07-14 20:08:55

+0

查看其他答案,你對迭代器進行了調整和返回是正確的,但是你不使用返回值,所以你的迭代器不再有效。 – schnaader 2009-07-14 20:13:20

+0

@gogole - 他說,算法上你不應該從列表中刪除元素..它與你的容器的實現無關。 – eduffy 2009-07-14 20:14:00

3

我想我看到一個問題,它是std :: list.erase()。擦除()使刪除的迭代器無效只有 - 所有其他部分的迭代器都是有效的。刪除它之後,您繼續在for語句中使用它 - 「elements_iter!= num_list.end()」。

爲了解決您可以使用的事實,即擦除()返回迭代器後擦除一個或結束如果擦除oiterator是最後一個。因此,替換行:

num_list.erase(elements_iter);

通過

elements_iter = num_list.erase(elements_iter);

如果您仍有問題,我建議您在Visual Studio下調試算法 - 它具有degub版本的STL,所以如果錯誤調試程序會在線路上停止導致問題。

4

我不知道你爲什麼在這裏使用一個列表。在vector上運行篩選並將素數保存在列表中更容易。

std::list<int> primes(int MAXN){ 
    std::list<int> result; 
    std::vector<bool> sieve(MAXN+1,true); 
    for(int i=2;i<=MAXN;i++){ 
    if(sieve[i]==true){ 
     result.push_back(i); 
     if((long long)i*i<=MAXN) //prevent integer overflow 
     for(int j=i*i;j<=MAXN;j+=i) 
      sieve[j]=false; 
    } 
    } 
    return result; 
} 
+0

通過對無限內存和無緩存未命中的簡單假設,使用列表將使算法更快。好吧,這些都不是小的假設:) – Brian 2009-07-14 20:49:52

+1

請注意,您只需要將少於或等於sqrt(MAXN)的素數的倍數交叉,並且您不是在2 * i處開始交叉,而是在i * i – 2009-07-14 21:00:16

+0

我不知道爲什麼我有2 :) – niteria 2009-07-14 21:06:26

2

我已經發布了更高效的篩子previously


這適用於我。從代碼中進行顯着修改:使用.erase(i++),否則迭代器將失效,並從列表中的連續位置開始multiple_sieve,而不是始終從頭開始。

#include <iostream> 
#include <list> 

template<typename T, typename L> 
void multiple_sieve(L &nums, 
     const typename L::iterator &begin, const typename L::iterator &end) { 
    T first = *begin; 
    typename L::iterator i(begin); 
    ++i; 
    while (i != end) 
     if (*i % first == 0) nums.erase(i++); 
     else ++i; 
} 

template<typename T, typename L> 
void prime_sieve(L &nums) { 
    typename L::iterator end = nums.end(); 
    for (typename L::iterator i(nums.begin()); i != end; ++i) 
     multiple_sieve<T, L>(nums, i, end); 
} 

int main(int argc, char **argv) { 
    std::list<int> list; 
    for (int i = 2; i < 1000; i++) 
     list.push_back(i); 
    prime_sieve<int, std::list<int> >(list); 
    for (std::list<int>::iterator i(list.begin()); 
      i != list.end(); i++) 
     std::cout << *i << std::endl; 
}