作者 : XiaXinyu
日期 :2021-10-01
给定一个正整数 n,请你求出 1∼n 中质数的个数。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中质数的个数。
数据范围
1≤n≤106
输入样例:
8
输出样例:
4
1.埃氏筛法
思路:将2—n中每一个数的倍数全部筛去,剩下的数就是2—n中的质数。
反证法证明:假设将2—n中每一个数的倍数全部筛去,剩下的数中有合数ai
由合数的基本概念可知那么ai一定是a1—ai-1中某个数的倍数,与假设矛盾
证毕
代码
import java.util.*;
public class Main{
static int[] p = new int[1000010];
static boolean[] s = new boolean[1000010];
static int cnt = 0;
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
for(int i = 2;i <= n;i ++){
if(! s[i]){
p[cnt ++] = i;
for(int j = i;j <= n;j += i) s[j] = true;
}
}
System.out.println(cnt);
}
}
时间复杂度
:O(nloglogn)
2.线性筛法——重点
在前面的埃氏筛法中有很多数被重复删除多次,因为一个合数可能是多个质数的倍数,使用线性筛法可以避免这种情况并且将时间复杂度降低到O(n)
埃氏筛法核心原理:每个合数只会被它的最小质因子筛去
代码和思路:
//2.线性筛法
import java.util.*;
public class Main{
static int[] p = new int[1000010];
static boolean[] s = new boolean[1000010];
static int cnt = 0;
static void get_primes(int n){
for(int i = 2;i <= n;i++){
if(!s[i]) p[cnt ++] = i;
for(int j = 0;i * p[j] <= n;j ++){
//1.p[j]是n的质因子,要确保i * p[j] <= n的原因是
//删除大于n的合数没有意义
s[p[j] * i] = true;
if(i % p[j] == 0) break;
//2.当i % p[j] == 0时说明p[j]是i的最小质因子,那么p[j]也
// 一定是p[j] * i的最小质因子
// 当i % p[j] != 0是说明p[j]不是i的最小质因子,但是p[j]
// 仍然是p[j] * i的最小质因子
// 所以不论哪种情况p[j]都一定是p[j] * i的最小质因子
//3.为什么当i % p[j] == 0是要跳出循环呢?
// 因为如果不跳出循环的话,p[j + 1] * i 的最小质因子就不是
// p[j + 1]了,因为i存在最小质因子p[j],这就导致了p[j + 1] * i
//并不是被自己的最小质因子给删除的,违背了线性筛法的原理,造成了
//重复删除的现象
//4.为什么2-n之间的合数都会被删干净呢?
// 因为每一个合数都存在一个最小质因子,就是这么简单!!!
}
}
System.out.print(cnt);
}
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
get_primes(n);
}
}