Don't try to run before you have learnt the art of walking.
Don't use fixed size arrays like int primes[100]
unless you have to.
One reason is that this requires you, the programmer, to determine the required size up front - for example by going to the nth Prime Page and finding out that there are 148933 primes below 2000000.
It also requires you, the programmer, to add additional checks to your code to ascertain that array accesses aren't exceeding the bounds of the array (unless you are using a language that does this for you, like Java or C#). Yet another reason is that it requires you to add code for book keeping, i.e. to track how many cells of the array are currently occupied.
Last but not least, allocating an array of 148933 integers as an automatic variable (i.e. on the stack) is likely to result in a crash because it blows the stack.
If you use std::vector<>
then all these headaches go away instantly, and your code becomes much simpler.
Start with a simple plan and implement the steps with separate pieces of code. It is easier to keep on top of things if every piece of code has a simple, well-defined responsibility. Things get more difficult if you entangle everything into one big bad clump.
For example, if you store the primes you found in a vector then this allows you to look at the numbers there to see if all is well, and perhaps compare them to known lists of primes (like The First 10,000 Primes, or the primes up to 1,000,000,000,000 at primos.mat.br). You can look, but you don't have to. If you intersperse everything with output code then you always have to look at all of them. If you just add them to the sum then you cannot see them unless you debug your program and follow each and every step.
Formulate your plan as pseudocode, so that you can take it in at a glance and understand it fully. If you don't have a plan, or if you don't understand it, then the result is very likely to be cr*p.
for each candidate n between 2 and 2000000
for each known prime p up to sqrt(n)
if p divides n
break
if no divisor p was found // must be a new prime
add n to the list of found primes
Obviously, the criterion 'if no divisor p was found' requires you to use a flag, like divisor_found
, that gets initialised to false
before the inner loop. Hence the first refinement:
for each candidate n between 2 and 2000000
divisor_found := false
for each known prime p up to sqrt(n)
if p divides n
divisor_found := true
break
if not divisor_found // must be a new prime
add n to the list of found primes
This can be implemented without further ado. The enumeration of the candidates can be improved by skipping some numbers that cannot possibly be prime, like multiples of two:
add 2 to the list of found primes
for each odd number between 3 and 2000000
...
This immediately cuts your workload in half, and it is the simplest example of a 'wheel'. For problems like this, it is very practical to skip multiples of 3 as well (mod 6 wheel) by starting with 5 and incrementing by 2 and 4 in an alternating fashion.
add 2 and 3 to the list of found primes
for n = 5 to 2000000 step 6 // 6 = 2 * 3
try n
try n + 2
Here, the trial division done in try
does not need to consider 2 or 3 as potential divisors, because your method of enumerating candidates already excludes all their multiples. Extension to skipping multiples of 5 as well is fairly straightforward.
If you do an expensive calculation like sqrt(n)
during each iteration of the innermost loop then your code gets slowed down to a crawl. n
doesn't change during the lifetime of the inner loop, so compute the value once in the loop header instead of needlessly repeating the computation.
Be that as it may, randomly trying different integer datatypes will get you nowhere. If values cannot become negative - as is the case here - then unsigned
should be your first choice. On current systems this usually corresponds to uint32_t
, which is more than enough for the piddling small numbers involved in this Euler task. You can save yourself some hassle by introducing a suitable typedef; that way you only need to change one single definition should need arise:
typedef std::uint32_t num_t;
typedef std::uint64_t sum_t;
num_t const N = 2000000;
...
std::vector<num_t> primes;
...
for (num_t n = 3; n <= N; n += 2)
...
sum_t sum = 0;
for (num_t p: primes)
sum += p;
I've added a separate sum_t
as well since a ballpark estimation of the sum puts it well beyond the capacity of a uint32_t
.
In any case you should seriously consider using the Sieve of Eratosthenes here. It is simpler than wheeled trial division and several orders of magnitude faster - even the simplest rendering should solve this Euler task in a few milliseconds.