说明: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-3,6k-2,6k+2,6k+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