素数问题

说明:1 不是素数 


Eratosthenes筛选

由于一个合数总是可以分解成若干个质数的乘积,那么如果把质数的倍数都去掉,那么剩下的就是质数了。Eratosthenes筛选法的思想特别简单:对于不超过n的每个非负整数p,删除 2p,3p,4p,...,当处理完所有数之后,还没有被删除的就是素数。如果用vis[i] 表示i已经被删除,则筛选法的代码可以写成:
 

public static void eratosthenes(int n){//筛选1-n的素数
    for(int i = 2; i <= n; i++) {
        for(int j = 2 * i; j <= n; j += i) {
            vis[j] = true;//标记为1的都是非素数
        }
    }
}
public class Main {
    private static final int MAX_COUNT = 10000000;
    private static boolean[] vis = new boolean[MAX_COUNT];//用于标记
    public static void main(String[] args) {
        eratosthenes(vis.length-1);
        show();
    }
    public static void eratosthenes(int n){//筛选1-n的素数
        for(int i = 2; i <= n; i++) {
            for(int j = 2 * i; j <= n; j += i) {
                vis[j] = true;//标记为1的都是非素数
            }
        }
    }

    private static void show() {
        int k = 0;
        for(int i = 2; i < 100; i++) {
            if(vis[i] != true) {
                k++;
                System.out.print(i+"\t");
                if( (k % 10) == 0) {
                    System.out.println();
                }
            }
        }
    }
}

优化:

尽管代码已经相当高效了,但仍然可以进行改进。首先,在 "对于不超过n的每个非负数p" 中,p可以限定为素数只需在第二重循环前一个判断 if(!vis[i]) 即可。另外,内层循环也不必从 i * 2 开始,它已经在i = 2时被筛选了。改进后代码如下:

static void eratosthenes2(int n){//筛选1-n的素
    int m = (int) Math.sqrt(n + 0.5);
    for(int i = 2; i <= m; i++){
        if(vis[i] != true){
            for(int j = i * i; j <= n; j += i)
                vis[j] = true;
        }
    }
}
public class Main {
    private static final int MAX_COUNT = 10000000;
    private static boolean[] vis = new boolean[MAX_COUNT];//用于标记 开始全为 false
    public static void main(String[] args) {
        eratosthenes2(vis.length - 1);
        show(100);
    }

    static void eratosthenes2(int n){//筛选1-n的素
        int m = (int) Math.sqrt(n + 0.5);
        for(int i = 2; i <= m; i++){
            if(vis[i] != true){
                for(int j = i * i; j <= n; j += i)
                    vis[j] = true;
            }
        }
    }
    private static void show(int n) {
        int k = 0;
        for(int i = 2; i < n; i++) {
            if(vis[i] != true) {
                k++;
                System.out.print(i+" ");
                if( (k % 10) == 0) {
                    System.out.println();
                }
            }
        }
    }
}

Eratosthenes筛选法虽然效率高,但是Eratosthenes筛选法做了许多无用功,一个数会被筛到好几次,最后的时间复杂度是O(nloglogn),对于普通素数算法而言已经非常高效了,但欧拉筛选法的时间复杂度仅仅为O(n)。


欧拉筛

public class Main {
    public static int Max = 100000000;
    /**用来存储筛选出来的素数*/
    public static int[] prime = new int[Max];
    /**用于标记*/
    public static byte[] vis = new byte[Max];
    private static int cnt = 0;
    public static void main(String[] args) {
        Euler_peime(Max-1);

        //输出100个
        for(int i = 0; i < 100; i++){
            System.out.print(prime[i]+" ");
        }
    }
    static void Euler_peime(int n){
        for(int i=2;i<=n;++i){
            if(vis[i] == 0){//没被标记的是素数
                prime[cnt++]=i;
                vis[i]=1;//vis[i]置为1或不置1都可以
            }
            for(int j=0;j<cnt;++j){
                if(i*prime[j]>n) {//判断是否越界
                    break;
                }
                vis[i*prime[j]]=1;//筛数
                if(i%prime[j]==0) {//时间复杂度为O(n)的关键!
                    break;
                }

            }
        }
    }
}

我们注意到,在用埃式筛法的同时,同一个数字也许会被筛选多次,比如6先被2筛选一次,再被3筛选一次,这样就浪费了很多不必要的时间,而欧拉筛法通过 if(i%prime[j]==0)break; 这一步就避免了重复筛选的发生,我们举个例子,比如,2先筛选了4,然后进行下一个循环,3筛选6和9,当我们执行到4的时候,可以发现,当 i==4 时,第一次运行到 if(i%prime[j]==0) 这一步的时候就直接 break; 掉了,这也就是说,当我们的合数进入循环时,其实它已经被之前的数筛选过了,所以当合数进入内层循环时,内层循环只执行了一次,从而减少了时间复杂度。
 


6倍原理

原理: 

6倍原理就是,素数只可能出现在6的倍数附近。

原因:

6的倍数以外的数是什么?6的倍数就是6k,6k附近的数,6k-3,6k-2,6k-1,6k,6k+1,6k+2,6k+3,那么不在6k左右的几个数是6k-36k-26k+26k+3,第一个和最后一个数是可以整除3,另两个数是可以整除2的,所以他们肯定不是素数。所以只有6的倍数附近的两个数才有可能是质数

为什么说可能是质数的,我举个反例,25,35,49..................,那具体是什么时候才有可能是非质数呢?观察一下 25((6-1)*(6-1))35((6-1)*(6+1))49((6+1)*(6+1))...

为什么呢?计算一下,(6k±1)(6p±1)=36kp±6p±6k±1,观看右边式子的 36kp±6p±6k 是6的倍数,所以他们的乘积一定是6的倍数附近的数。

//高效判断素数
private boolean isPrime(int num) {
    if (num == 2 || num == 3) {
        return true;
    }

    //如果不在6的倍数附近,肯定不是素数
    if (num % 6 != 1 && num % 6 != 5) {
        return false;
    }
    //对6倍数附近的数进行判断
    for (int i = 5; i <= Math.sqrt(num); i += 6) {
        if (num % i == 0 || num % (i + 2) == 0) {
            return false;
        }
    }
    return true;
}

题: 洛谷:P3383 【模板】线性筛素数

c++

6倍原理

#include<iostream>
#include<cmath>
using namespace std;
  bool judgementPrimes(int k) {
	//单独判断 1不是素数
	if (k == 1) {
		return false;
	}
	//6 以下的素数 2 和 3
	if (k == 2 || k == 3) {
		return true;
	}
	//如果不在6的倍数附近(就是6的倍左右的那两个数字),肯定不是素数
	if (k % 6 != 1 && k % 6 != 5) {
		return false;
	}
	//对6倍数附近的数进行判断
	for (register int i = 5; i <= sqrt(k); i += 6) {
		if (k%i == 0 || k % (i + 2) == 0) {
			return false;
		}
	}
	return true;
}

int main() {
	int n;
	int m;
	int temp;
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		cin >> temp;
		if (judgementPrimes(temp)) {
			cout << "Yes" << endl;
		}
		else {
			cout << "No" << endl;
		}
	}
	return 0;
}

 Eratosthenes

#include <iostream>
#include <cmath>
using namespace std;
bool vis[10000010];//标记
void eratosthenes(int n) {//筛选1-n的素
	int m = (int)sqrt(n + 0.5);
	for (int i = 2; i <= m; i++) {
		if (vis[i] != true) {
			for (int j = i * i; j <= n; j += i)
				vis[j] = true;
		}
	}
}


int main() {
	vis[1] = true;
	eratosthenes(10000010 - 1);
	int n;
	int m;
	int temp;
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		cin >> temp;
		if (vis[temp] == false) {
			cout << "Yes" << endl;
		}else {
			cout << "No" << endl;
		}
	}
	return 0;
}

java

6倍原理

import java.io.*;
public class Main {
    private static int n; //查询范围
    private static int m;//查询个数
    public static void main(String[] args) throws IOException {
        StreamTokenizer input = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

        input.nextToken();
        int n = (int)input.nval;
        input.nextToken();
        int m = (int)input.nval;

        for(int i = 1; i <= m; i++){
            input.nextToken();
            if(judgementPrimes((int)input.nval)){
                System.out.println("Yes");
            }else {
                System.out.println("No");
            }
        }
    }

    /**
     * 采用 6倍原理 判断素数
     */
    public static boolean judgementPrimes(int k){
        //单独判断 1不是素数
        if(k == 1){
            return false;
        }
        //6 以下的素数 2 和 3
        if(k == 2 || k == 3){
            return true;
        }
        //如果不在6的倍数附近(就是6的倍左右的那两个数字),肯定不是素数
        if(k % 6 != 1 && k % 6 != 5){
            return false;
        }
        //对6倍数附近的数进行判断
        for(int i = 5; i <= Math.sqrt(k); i +=6){
            if(k%i == 0 || k%(i+2) == 0){
                return false;
            }
        }
        return true;
    }
}

 Eratosthenes 

import java.io.*;
public class Main {
    public static int MAX_SIZE = 10000010;
    public static boolean[] vis = new boolean[MAX_SIZE];
    public static void eratosthenes(int n) {//筛选1-n的素
        int m = (int)Math.sqrt(n + 0.5);
        for (int i = 2; i <= m; i++) {
            if (vis[i] != true) {
                for (int j = i * i; j <= n; j += i)
                    vis[j] = true;
            }
        }
    }
    public static void main(String[] args) throws IOException {
        StreamTokenizer input = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        vis[1] = true;
        eratosthenes(10000010 - 1);

        input.nextToken();
        int n = (int)input.nval;
        input.nextToken();
        int m = (int)input.nval;
        int temp;
        for (int i = 0; i < m; i++) {
            input.nextToken();
            temp = (int)input.nval;
            if (vis[temp] == false) {
                System.out.println("Yes");
            }else {
                System.out.println("No");
            }
        }
    }
}

注:java 一定不要用 Scanner  会过不了,读入速度太慢。


参考:

(最高性能)高效求一个数是否为素数,6倍原理:https://blog.csdn.net/qq_36523667/article/details/78545682

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值