leedcode题目204:计数质数(素数)
给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。
例如:
输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
方法一、暴力方法
public class SuShu {
public static void main(String[] args) {
System.out.println(bf(100));
}
//暴力算法
public static int bf(int n){
//定义用来计数的变量
int count=0;
//因为素数0,1除外,所以i从2开始,100以内的数所以不超过100
for(int i=2;i<=n;i++){
count+=isPrime(i)?1:0;
}
return count;
}
private static boolean isPrime(int m) {
//因为素数只能被1和自身整除,所以i从2开始到根号m之间如果能整除就不是素数
for(int i=2;i <=Math.sqrt(m);i++){
if(m % i==0){
return false;
}
}
System.out.println("m"+m);
return true;
}
}
时间复杂度为n * √m
这里使用开根号的方法,可以减少一定的复杂度因为:
一个数N的最小质因子,必定小于开根号N:
数学表达:a*b=N,若a<开根号N,b必定>开根号N,所以只要求2~开根号N,即可判断N是不是素数。
以16这个数为例子:
16是一个合数(就是除了自身和1之外还能被其他数整除和素数相对)
16有两个因子相乘得到,这里可以写为因子1和因子2。
因子1有1,2,3,4。因子2有16,8,6,4。因子1是始终小于等于根号下16的。而我们只需要筛选出因子1即可。例如:28和82的结果是一样的,能被2整除自然也能被8整除。
这样做的好处就是:
从2开始筛选素数,只要筛选出有因子1能把它整除就能证明它不是素数。这样就节约了很多的计算量,比如>10000的数,你进行一百多次循环就可以判断出它是否是素数了,这样就从10000直接降到了100。
方法二、埃式筛选
埃拉托斯特尼筛法,简称埃氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法。算法思想:给出要筛数值的范围,找出其以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数3去筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去…
代码如下:
public class SuShu {
public static void main(String[] args) {
System.out.println(eratosthenes(100));
}
public static int eratosthenes(int n){
boolean[] isPrime=new boolean[n];//默认false代表素数
int count=0;
for(int i=2;i<n;i++){
if(!isPrime[i]){//如果是素数
count++;//记录素数个数
if(i<=Math.sqrt(n)){//筛到 sqrt(n) 就可以停止筛了,后面都是已经筛过的数字了。
for(int j=2*i;j<n;j+=i){
isPrime[j]=true;//标记为true
}
}
}
}
return count;
}
}
时间复杂度为:O(nloglogn)
还可以进行优化,代码如下:
public static int eratosthenes(int n){
boolean[] isPrime=new boolean[n];//默认false代表素数
int count=0;
for(int i=2;i<n;i++){
if(!isPrime[i]){//如果是素数
count++;//进行计数
if(i<=Math.sqrt(n)){//筛到 sqrt(n) 就可以停止筛了,后面都是已经筛过的数字了。
for(int j=i*i;j<n;j+=i){
isPrime[j]=true;
}
}
}
}
return count;
}
只是将2i优化为ii。例如当统计25里有多少素数的时候,当i=2时,j=22=4,j=4+2=6,j=6+2=8,j=8+2=10…。而当i=3时,j=23=6,j=6+3=9,j=9+3=12,j=12+3=15…。当i=4的时候,j=24=8,j=8+4=12,j=12+4=16,j=16+4=20…。可以看出当在遍历i=2的时候就已经给isPrime[6]和isPrime[8]标记为true了,而再遍历i=3的时候,我们还重复标记isPrime[6]。当遍历i=4的时候,还重复标记isPrime[6],Prime[8],Prime[12]。所以我们只需要从ii开始进行遍历,这样就不会有重复标记的情况。