Question

I have a programming assignment to write a program in C++ that finds all primes less than n (user input). One half of the assignment involves the Sieve of Eratosthenes. My code is working (read: assignment is complete), but before I edited the output, it was unconditionally printing out n-3, n-2, and n-1 as primes even if they were not prime. I'm not sure why this is happening. I'd appreciate a bit of feedback and ideas as to why the program is acting the way it is. Here is the unaltered code:

Please note that I am using a ListNode class and a LinkedList class, both of which are fully functional. EDIT: partial main added; notice the second item in the for loop is size-3. If it's left at size, the program outputs 3 extra non-primes.

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;
   }
Était-ce utile?

La solution 2

From your code:

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

This loop is responsible for going through all numbers i that are integer multiples of the prime number p and marking them not prime, as I understand. Why are you stopping on the condition i < item - 2? This would be fine if i were your index for the my_list and marked lists, but in this case it's not; it's the actual number you're marking not prime. I suspect this is why you're getting numbers near your limit (item) that are marked as prime—your loop here exits before i ever gets to those numbers!

By the way, you could do this as a for loop instead, which would be easier to read. The for loop has the meaning "go through each element in a set" (whether that's consecutive integers, or every nth integer, or elements in an array/list/deque, etc.), so a programmer reading your code knows that immediately and doesn't have to figure it out from your while loop.

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

(This is the same as your original code, no fixes applied).


Some general comments to improve your algorithm/code:

Try using more descriptive variable names. Your use of i two times to mean different things is confusing, and in general single letters don't mean much as to what the variable represents (although sometimes they're sufficient, e.g. a for loop where i is the index of a list/array).

Also, you're looping over your list a lot more than you need to. The minimum a sieve of Eratosthenes algorithm needs is two nested for loops (not including initializing a list/array to all true).

One example of where you're doing more work than necessary is that you're looping starting from index 0 to find the next p to use—instead of just remembering where your current p is and starting from there. You wouldn't even need to check my_list[i] > p in that case, since you know you'd be beyond it to start off. Also, your last loop could break; early and avoid continuing on after it finds a non-prime (and I'm not sure what the point of it is).

Nikola Mitev's second answer is a more efficient and more readable implementation of the sieve (but replace sqrt(upperBound) with upperBound/2 for it to work correctly; the reason for upperBound/2 should be pretty clear from the way the Sieve works), although he didn't really give much comment or explanation on it. The first loop is "go through every number up to upperBound"; inside it, "if the current number is a prime, go through all the multiples of that prime and mark them non-prime". After that innerloop executes, the outer loop continues, going through the next numbers—no need to start from the beginning, or even type out another for loop, to find the next prime.

EDIT: sqrt(upperBound) is correct. I wasn't thinking about it carefully enough.

Autres conseils

Complete method

    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<<" ";
          }
    }

Why don't you work with array of booleans for simplicity starting from index 2, and when you will print the result, you will print indices with value of true

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top