线性筛法:
假设有一个非质数 x,那么这个数可以被表示为一个最小质因数和一个因子相乘的形式
如 x = 12 ,那么 x = 2*6
其中:2 就是 12 的最小质因数, 6 就是另一个因子
线性筛法就是利用每个数的最小质因数筛掉这个非质数,如12就要用2筛掉,18就要用3筛掉
step1:
遍历2-n之间的所有数,一是为了遍历2-n之间的所有的质数,二也是为了找到和质数相乘的另一个数。
step2:
将质数(没有被筛掉的数就是质数)加入到primes数组中。
step3:
遍历primes数组,从小到大遍历所有2-n之间的质数(要满足这个质数和另一个因数相乘的值要小于n)
step4:
用primes[j]筛去 i *primes[j] 这个数
step5:
因为primes数组记录质数是从小到大记录的质数,因此直到 i % primes[j] == 0,primes[j]都是
i * primes[j] 这个数的最小质因数,当 i%primes[j] == 0 时,下一次的primes[j+1] 就不是 i* primes[j]的最小质因数了,所以要break。
题目如下:
给定一个正整数 n,请你求出 1∼n 中质数的个数。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中质数的个数。
数据范围
1≤n≤106
解答代码如下:
#include<iostream>
#include<cstring>using namespace std;const int N = 1000010;int primes[N];
bool st[N];
int n,cnt;int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> n;//线性筛法的宗旨就是只能以一个非质数的最小质因子去筛一个非质数//如12 = 2*2*3,它的最小质因子就是2,因此,12只能被2筛掉,也就是以st[2*6]的方式筛掉//而不是以st[3*4]的方式筛掉for (int i = 2;i<=n;++i)//列举所有2-n之间的数//n = a * b//a代表n的最小质因数,b代表另一个因子//如12的最小质因数就是2,那么12 = 2 * 6//这里的a就是2,b就是6//这里遍历2-n之间的所有数,既是为了找到2-n之间的每个数的最小质因数//也是为了找到和最小质因子相乘的另一个数,如12 = 2*6中的6{if (st[i] == false)primes[cnt++] = i;//在指数的数组中加入数组//没有被标记为非质数的数就是质数for (int j = 0;primes[j] <= n/i;++j)//从小到大遍历所有的质数,找到满足primes[j]*i <=n的质数{st[primes[j] * i] =true;//因为是从小到大遍历的质数,因此此时的primes[j]一定是primes[j]*i //这个数的最小质因子,那么就用primes[j]这个最小质因子去把primes[j]*i这个数筛掉if (i%primes[j] == 0)//这一句话的作用,是保证每个数都是被自己的最小质因数筛掉,且只会被筛一次//如果i % primes[j] != 0://说明primes[j]不是 i 的最小质因数,//且primes[j]一定小于i的最小质因数,因为primes[j]是从小到大遍历的所有质数//那么primes[j]就一定是primes[j]*i 的最小质因数//可以这么看primes[j]*i = primes[j]*(i的质因数乘积式),如果primes[j]小于//i的任何一个质因数,那么primes[j]就一定是primes[j]*i的最小质因数//这样的话,primes[j+1]的最坏结局就是i%primes[j+1] == 0//也就是primes[j+1]是i的最小质因数,那么primes[j+1]*依然可以由primes[j+1]来筛掉//但是对于primes[j+2]而言,primes[j+2]就不会是primes[j+2]*i的最小质因数了//因此,,如果i % primes[j] == 0,说明下一次循环到的primes[j+1]就不会是//primes[j+1]*i的最小质因数了,所以要break//如果i % primes[j] != 0,说明下一次循环到的primes[j+1]一定会是primes[j+1]*i的最小质因数//(这里的一定是最小质因数分两种情况://1.primes[j]小于i的最小质因数,那么primes[j]是primes[j]*i的最小质因数了//2.primes[j]等于i的最小质因数,那么primes[j]是primes[j]*i的最小质因数)//所以 i % primes[j] != 0不用break;break;}}cout << cnt ;return 0;
}
(1)为什么要for (int j = 0;primes[j] <= n/i;++j)
因为这个循环的目的是为了筛掉小于 n 的非质数(剩下的就是质数),而这个非质数是用 i * primes[j] 来确定的,因此,如果 primes[j] * i > n 的话,那么筛掉的非质数就不是小于 n 的了
(2)if(i % primes[j] == 0)break;
这一语句是保证每一个非质数是被它的最小质因数筛掉的。看代码中的注释