Calculate the number of prime numbers in N (N> = 3) by using an algorithm.
First, let's talk about the definition of prime numbers. What is a prime number? Except 1 and itself, it cannot be divisible by other natural numbers (except 0 ).
It is called a prime number (prime number); otherwise it is called a union number.
According to the definition of prime numbers, to solve this problem, I first thought of traversing each odd number from 3 to N, and then dividing by three
The odd number between the root number N can be used to calculate the number of prime numbers.
The following code is compiled:
(The code is written in C ++)
# Include <iostream> # include <time. h> using namespace std; const int N = 1000000; int compuPrimeN (int); int main (char argc, char * argv []) {int iTimeS = clock (); int iNum = compuPrimeN (N); int iTimeE = clock (); cout <iNum <endl; cout <"algorithm time: "<iTimeE-iTimeS <" millisecond "<endl; getchar (); return 0;} int compuPrimeN (int maxNum) {// algorithm 1int iNum = 1; // start with 2 bool bPrime = true; for (int I = 3; I <= maxNum; I + = 2) {bPrime = true; for (int j = 3; j <= (int) sqrt (I); j + = 2) {if (I % j = 0) {bPrime = false; break ;}} if (bPrime) iNum ++;} return iNum ;}
After running:
It can be seen that the algorithm performance is not very good, and there is still much room for optimization in time.
So how should we optimize it?
First, I want to see if I can remove the multiples of 2, but later
It is found that the first remainder in the second loop is 3, so the multiples of 3 are actually calculated only once.
It filters out, and there is no need to think further.
Later I thought, in the second loop, 3 times out of the remainder. If the loop is not exceeded, 6, 9, and so on
You do not need to continue to get the remainder. Similarly, if you get the remainder of 5, you should not continue to get the remainder of 10, 15... because the remainder is obtained.
5 is not 0, so the remainder 10, 15 is certainly not 0. In other words, the remainder should not be actually a sum !!
Why? Because if it is a combination of numbers, it is bound to be able to obtain the remainder of a number smaller than its own number, that is
Previously, we wanted to filter out the numbers that do not want to get the remainder. In this way, we only need to get the remainder in the second loop.
It can be determined by a smaller prime number than its root number! However, before we calculate the prime number
Find out, so we just need to record it !!
As a result, I have rewritten the compuPrimeN () function and written 2nd algorithms:
Int compuPrimeN (int maxNum) {// algorithm 2int iNum = 1; // record the total number of prime numbers int iRecN = 1; // record the number of prime numbers in the array bool bPrimeN = true; int sqrtMaxN = (int) sqrt (maxNum); // we want to record the prime numbers smaller than sqrtMaxN. In order to make the space allocation optimal, the size is x/ln (x) * 1.2, // because scientists have found an approximate formula x/ln (x) for calculating the approximate range of prime numbers. // to avoid array out-of-bounds, add a 20% range. // note that maxNum is a special case when it is 3, because ln (root 3) is 0int * iPrime = new int [maxNum = 3? 1: (int) (float) sqrtMaxN/log (sqrtMaxN) * 1.2)]; for (int I = 3; I <= maxNum; I + = 2) {bPrimeN = true; // you only need to obtain the prime number in the remainder range. for (int j = 1; j <iRecN; j ++) {if (I % iPrime [j] = 0) {bPrimeN = false; break;} if (bPrimeN) {if (I <= sqrtMaxN) {iPrime [iRecN] = I; iRecN ++; iNum = iRecN;} elseiNum ++ ;}} delete iPrime; return iNum ;}
After running:
Look, after optimization, the algorithm's time performance is about 19 times better than the original one,
Can it be faster?
I think it works theoretically, because the previous algorithms use an idea,
Filter out multiples of 2 and 3 in advance. If we can add multiples of 5, 7, and 11
Isn't it faster to filter it out beforehand?
Why is there no 9 here, because the multiples of 9 are the multiples of 3, then? It seems
What we found is a bit similar to the idea of algorithm 2. If we can filter out
If the prime number is a multiple, isn't it possible to filter out many aggregate numbers? For this prime number + 1,
There are two situations: one is the sum of filtered out, and the other is the prime number,
Otherwise, it should be filtered out before !! In the process of filtering,
Isn't it the prime number that we want to count the unfiltered data that we encounter?
In this way, can time performance be further optimized? Yes, but in advance
Filtering out so many combinations and recording their behaviors consumes a lot
Space. This is a typical space change time !!
So the algorithm 3 I wrote was born, as follows:
Int compuPrimeN (int maxNum) {// algorithm 3 // use a bool-type large array to record. true indicates a prime number, and false indicates an even number. // calculate the number of prime numbers, so the first two can be ignored. bool * bArray = new bool [maxNum + 1]; for (int I = 2; I <= maxNum; I ++) bArray [I] = true; int iNum = 0; for (int I = 2; I <= maxNum; I ++) {// Replace the following combination with falseif (bArray [I]) {iNum ++; for (int j = I + I; j <= maxNum; j + = I) {bArray [j] = false ;}} delete bArray; return iNum ;}
After running
Wow! I did not expect that the algorithm time could be optimized so quickly !! However, it seems like the space consumed
There are a lot of storage. It seems a waste to use only bool-type array records. Can I use 0 or 1 on each bit?
To replace records?
So I wrote the following algorithm:
Int compuPrimeN (int maxNum) {// algorithm 4 // each digit 0 or 1 is used to represent the sum and prime number respectively. // The advantage is that the maximum int size of memory space = maxNum % 8 = 0? MaxNum/8: maxNum/8 + 1; unsigned char * array = new unsigned char [size]; for (int I = 0; I <size; I ++) array [I] = 127; int iNum = 0, iBit = 0, index = 0; for (int I = 2; I <= maxNum; I ++) {index = I/8; (iBit = I % 8) = 0? IBit = 7, index --: iBit --; if (array [index] & (1 <iBit) {iNum ++; for (int j = I + I; j <= maxNum; j + = I) {index = j/8; (iBit = j % 8) = 0? IBit = 7, index --: iBit --; array [index] = array [index] & (~ (1 <iBit) ;}} delete array; return iNum ;}
Running result
Although binary computing makes the time performance slower than algorithm 3,
However, if bit is used to record the prime number or sum, the storage space is changed to the original 1/8,
The advantage is self-evident. If there is no memory space problem, so is algorithm 3.
Naturally, if you have strict requirements on memory space, algorithm 2 is the best
Preferred.
// Configure //--------------------------------------------------------------------------------------------------------------------
However, in addition to the above four algorithms, I came up with a 5th method that is almost cheating.
The question may cause criticism, but in practical application, it is a very useful method, which contains
It is an important idea. I call it "change from known to unknown "!!
The idea is: Save the known data in a certain format, and read the data once as needed.
The result is obtained. The algorithm time is O (1 ).
For example, in practice, we only need to use the number of prime numbers less than N <= 0.1 billion N.
(N can increase in the computer storage range when there are more requirements, but the pre-processing time will also increase ),
Then we can first call the following function to store the data of the number of prime numbers in N (N <= 0.1 billion) in binary format.
Void savPrimeN (int maxNum) {ofstream ofPrimeF ("PrimeNum. data ", ios: binary); int iNum = 0; // write 0 twice in advance, respectively as the number of prime numbers within 0, 1 for (int I = 0; I <2; I ++) ofPrimeF. write (const char *) (& iNum), sizeof (int); // use a bool-type large array to record data. true indicates a prime number, false is an even number. // The first two values can be ignored because the number of prime numbers is obtained. bool * bArray = new bool [maxNum + 1]; for (int I = 2; I <= maxNum; I ++) bArray [I] = true; int sizeInt = sizeof (int); for (int I = 2; I <= maxNum; I ++) {// Replace the combination number following falseif (bArray [I]) {iNum ++; for (int j = I + I; j <= maxNum; j + = I) {bArray [j] = false ;}} ofPrimeF. write (char *) (& iNum), sizeInt);} delete bArray; ofPrimeF. close ();}
Now we have stored the number of prime numbers in N (N <= 0.1 billion) to the binary file in every 4 bytes.
When we calculate the number of prime numbers within N (N <= 0.1 billion), we only need to read the corresponding data in the binary file.
As follows:
Int compuPrimeN (int maxNum) {// algorithm 5 ifstream ifPrimeN ("PrimeNum. data ", ios: binary); int iNum = 0; ifPrimeN. seekg (maxNum * 4, ios: beg); ifPrimeN. read (char *) (& iNum), sizeof (iNum); ifPrimeN. close (); return iNum ;}
Check the current computing time:
Because clock () function compute starts to call the function, the CPU usage time is accurate to milliseconds,
This means that the algorithm time cannot exceed 1 ms !! All of these benefits us.
A so-called "Database" created by myself, with this "Database", as long as N <= 0.1 billion,
We have no pressure on the performance of computing time !!!
Summary:
In thinking and coding, I deeply realized the importance of algorithm optimization.
A good programmer must understand that algorithms are the soul of a program !!
Calculate and output the square root of all prime numbers between 3 and n
# Include <stdio. h>
# Include <math. h>
Double fun (int n)
{
Int m = 3, I;
Double s = 0;
While (m <= n ){
For (I = 3; I <= sqrt (m); I = I + 2)
If (m % I = 0)
Break;
If (I> sqrt (m) & m! = 1)
S + = sqrt (m );
M + = 2;
}
Return s;
}
Void main ()
{
Int n;
While (scanf ("% d", & n )! = EOF ){
If (n <3 ){
Printf ("enter a number greater than 2 \ n ");
Continue;
}
Printf ("% lf \ n", fun (n ));
}
}
Programming in C language: Number of prime numbers within n (n <10 ^ 8)
I wrote one file, and it only took 13 seconds to write the file.
NCount = 5761455
Time = 13 s
# Include <stdio. h>
# Include <time. h>
Void main ()
{
Time_t tBegin;
Time (& tBegin );
Time_t tEnd;
Int nMax = 100000000;
Bool * pByte = new bool [nMax + 1]; // you can specify a value of 0 for each allocation.
Int I = 0, j = 0;
Int nCount = 0;
If (pByte = NULL)
{
Printf ("memory allocation failed! ");
}
// Assume that all the numbers are prime numbers.
For (I = 0; I <nMax; I ++)
{
PByte [I] = true;
}
PByte [0] = false; // 0 is not a prime number.
PByte [1] = false; // 1 is not a prime number
PByte [2] = true; // 2 is a prime number.
// Filter. All even numbers are not prime numbers (except 2)
For (I = 4; I <nMax; I + = 2)
{
PByte [I] = false;
}
// Filter all prime numbers. Integer multiples are not prime numbers.
For (I = 3; I <nMax; I ++)
{
If (pByte [I])
{
J = I;
While (j + = 2 * I) <nMax)
{
PByte [j] = false;
}
}
}
// Output result
FILE * file;
If (file = fopen ("Test.txt", "a") = NULL)
{
Printf ("An error occurred while opening the file .");
Return;
}
NCount = 0;
For (I = 2; I <nMax; I ++)
{
If (pByte [I])
{
: Fprintf (file, "% d \ n", I );
NCount ++;
}
}
Fprintf (file, "nCount = % d \ n", nCount );
Time (& tEnd );
Fprintf (file, "Time = % ds \ n", tEnd-tBegin );
Fclose (file );
Delete [] pByte;
}