我有一个编程任务,用C++编写一个程序,找到所有小于n(用户输入)的素数。任务的一半涉及埃拉托色尼筛。我的代码正在工作(阅读:赋值已完成),但在我编辑输出之前,它无条件地打印出 n-3、n-2 和 n-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
上停下来?如果i
是my_list
和marked
列表的索引,那就好了,但在这种情况下,它不是;这是您标记的实际数字,而不是素数。我怀疑这就是为什么你会得到接近极限(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)的第二个答案是更高效,更易读的筛子实现(但用),尽管他并没有真正给出太多评论或解释。第一个循环是"遍历每个数字直到上限";在里面,"如果当前数字是素数,请遍历该素数的所有倍数并将它们标记为非素数"。在内循环执行之后,外循环继续,遍历下一个数字——无需从头开始,甚至无需键入另一个 for 循环,即可找到下一个素数。upperBound/2
替换sqrt(upperBound)
才能正常工作;upperBound/2
的原因应该从筛子的工作方式中很清楚
编辑:sqrt(upperBound)
是正确的。我没仔细想清楚。
为什么不从索引 2 开始使用布尔数组以简化操作,当您打印结果时,您将打印值为 true 的索引