链表 - C++埃拉托色尼筛发现 3 个素数太多

linked list - C++ Sieve of Eratosthenes finding 3 too many primes

本文关键字:太多 发现 C++ 埃拉托 链表      更新时间:2023-10-16

我有一个编程任务,用C++编写一个程序,找到所有小于n(用户输入)的素数。任务的一半涉及埃拉托色尼筛。我的代码正在工作(阅读:赋值已完成),但在我编辑输出之前,它无条件地打印出 n-3、n-2n-1 作为素数,即使它们不是素数。我不确定为什么会发生这种情况。我希望能提供一些关于为什么程序如此运行的反馈和想法。以下是未更改的代码:

请注意,我使用的是ListNode类和LinkedList类,两者都是功能齐全的。编辑:部分主要添加;请注意,FOR 循环中的第二项是大小为 3。如果保持大小不变,程序将输出 3 个额外的非素数。

int main()
{
    for(int i = 0; i<my_list.size()-3; i++)
    {
        if(marked[i]==true)
            cout<<my_list[i]<<"n";
    }
}
void eratosthenes(int item)
{
   bool run=true;
   int p=2, count=0;
   for(int i=2; i<=item; i++)
   {
      my_list.append(i);    // Entire list is filled with integers from 2 to n
      marked.append(true);  // Entire list is filled with true entries
   }
   while(run==true&&(2*p)<item)
   {
      count = 0;
      int i = (2*p);
      do {
         marked[i-2]=false;       // marked values are false and not prime
         i+=p;
      } while(i<item-2);
      for(int i=0; i<item-2; i++) // i starts at 0  and increments by 1 
      {                           // each time through the loop
         if(my_list[i]>p)
         {
            if(marked[i]==true)   // If a value stored in a node is true  
            {                     //   (prime), it becomes the new p.
               p=my_list[i];      //   The loop is then broken. 
               break;
            }
         }
      }
      for(int j=1; j<item-2; j++)
      {
         if(marked[j]==false)
         {
            count=1;
         }
      }
      if(count==0)
         run=false;
   }

完整方法

    void Eratosthenes(int upperBound)
    {
        bool Prime[upperBound];
        for(int i = 0;i<upperBound;i++)
         Prime[i]=true;
        for (int i = 2; i <= sqrt(upperBound); i++) 
         {
             if (Prime[i]) 
             {
                for (int j = i * 2; j < upperBound; j += i) 
                    Prime[j] = false;
              }
         }
         for(int i=2;i<upperBound;i++)
         {
                 if(Prime[i]==true)
                 cout<<i<<" ";
          }
    }

从你的代码:

  do{
     marked[i-2]=false;//marked values are false and not prime
     i+=p;
  }while(i<item-2);

据我了解,这个循环负责遍历素数p的整数倍的所有数字i并将它们标记为非素数。你为什么要在条件i < item - 2上停下来?如果imy_listmarked列表的索引,那就好了,但在这种情况下,它不是;这是您标记的实际数字,而不是素数。我怀疑这就是为什么你会得到接近极限(item)的数字,这些数字被标记为素数——你的循环在i到达这些数字之前就退出了!

顺便说一下,你可以把它作为一个 for 循环来代替,这样更容易阅读。for 循环的意思是"遍历集合中的每个元素"(无论是连续整数,还是每个第 n 个整数,还是数组/列表/双端格式中的元素等),因此阅读代码的程序员立即知道这一点,而不必从 while 循环中找出来。

// mark every multiple of the current prime as not prime
for(int i = 2*p; i < item - 2; i += p)
{
    marked[i-2] = false;
}

(这与原始代码相同,未应用修复程序)。


一些改进算法/代码的一般评论:

尝试使用更具描述性的变量名称。你使用i两次来表示不同的东西是令人困惑的,一般来说,单个字母对变量代表什么没有多大意义(尽管有时它们就足够了,例如 for 循环,其中 i 是列表/数组的索引)。

此外,您循环浏览列表的次数比您需要的要多得多。埃拉托色尼算法的筛子需要的最小值是两个嵌套的 for 循环(不包括将列表/数组初始化为所有true)。

执行的工作过多的一个例子是,您从索引 0 开始循环以查找要使用的下一个p,而不仅仅是记住当前p的位置并从那里开始。在这种情况下,您甚至不需要检查my_list[i] > p,因为您知道自己一开始就超出了它的范围。此外,您的最后一个循环可能会提前break;,并避免在找到非素数后继续(我不确定它的意义何在)。

尼古拉·米捷夫(Nikola Mitev)的第二个答案是更高效,更易读的筛子实现(但用upperBound/2替换sqrt(upperBound)才能正常工作;upperBound/2的原因应该从筛子的工作方式中很清楚),尽管他并没有真正给出太多评论或解释。第一个循环是"遍历每个数字直到上限";在里面,"如果当前数字是素数,请遍历该素数的所有倍数并将它们标记为非素数"。在内循环执行之后,外循环继续,遍历下一个数字——无需从头开始,甚至无需键入另一个 for 循环,即可找到下一个素数。

编辑:sqrt(upperBound)是正确的。我没仔细想清楚。

为什么不从索引 2 开始使用布尔数组以简化操作,当您打印结果时,您将打印值为 true 的索引