The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent windowed sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent windowed sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent windowed sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent segmentedwindowed sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent segmented sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent windowed sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
- 2.8k
- 1
- 13
- 30
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent segmented sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent segmented sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
The answers given so far address some inconsequential inefficiencies but they miss the big picture, which is that the SPOJ puzzle in question is designed such that pundits need to invent segmented sieving in order to solve it.
In C/C++ you can actually get away with writing a blazingly fast full sieve and thus solve the problem with superior firepower instead of smarts (2 seconds for sieving up to 2^32-1 on a single core, vs. 15 to 30 seconds for a simple straightforward sieve without all the bells and whistles), but in most other languages that's not an option because you'll get hit with a timeout.
Hence the key is to do only the work requested, which is to sieve the given range (and not all numbers less than the upper end of the range). In order to do that it's also necessary to sieve the small handful of primes up to the square root of the upper limit of the range, but that's peanuts. The whole thing takes only a few milliseconds, even for the most simple-minded implementation using a non-optimising compiler.
Clarification: consider a test case of maximal size at the upper end of the possible range, with m = 999900000, n = 100000000. There are only 100001 numbers in that range but a simple sieve cannot give answers about the range between m and n without processing everything before m as well. This means sieving 10^9 numbers instead of 10^5, an overkill of four orders of magnitude. For smaller ranges the overkill ratio gets even worse.
On the other hand, a windowed/segmented sieve only needs to process the potential prime factors up to sqrt(max(n)) and the actual input ranges. The potential factors up to sqrt(10^9) = 31622 need to be sieved only once, which reduces the number of required operations in the worst-case example above from 10^9 to 10^5 + peanuts, and for the general case to (combined input size + peanuts).
Sieving a range of numbers between m and n involves iterating over all primes (potential factors) up to sqrt(n); for each small prime p, compute the start offset within the window using a bit of modulo arithmetic, then start crossing off numbers as usual (with stride p for odds-only sieves, or 2 * p for 'full' sieves that also hold even numbers).
More detail - including C++ code for a segmented sieve function and a link to various compilable test programs - is given in an answer to the same question elsewhere. The code shown there is for segmented sieving up to 2^64-1, meaning it can be simplified a lot for use in the SPOJ problem.
Using vector<bool>
is actually a pessimisation because of the bit references bandied about under the hood. But who cares whether your code is only a hundred times as fast as necessary, instead of a thousand times as fast? As long as the code is fast enough, the only things that count are simplicity and clarity.
- 2.8k
- 1
- 13
- 30