素数判定

一、如果求某一范围所有素数,或者所判定素数较小,数组可以开下,就可以用筛选法

埃拉特斯特尼筛法:时间复杂度为O(n loglog n)
从2开始,将每个质数的倍数都标记成合数,以达到筛选素数的目的

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#pragma GCC diagnostic error "-std=c++11"
using namespace std;
typedef long long ll;
const int mod = 1000000009;
const int N = 1e5 + 10;

#define MAX 100000 
long long su[MAX], cnt; //su存放素数
bool isprime[MAX];      //判断一个数是否素数

void prime()
{
    cnt = 1;
    memset(isprime, 1, sizeof(isprime)); 
    isprime[0] = isprime[1] = 0;         
    for (long long i = 2; i <= MAX; i++)
    {
        if (isprime[i]) 
        {
            su[cnt++] = i;
        }
        for (long long j = i * 2; j <= MAX; j += i) 
        {
            isprime[j] = 0;
        }
    }
}
int main()
{
    prime();
    d(su[56]);
    return 0;
}

欧拉筛
在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复筛选的目的。线性O(n)的复杂度

int prime[maxn];
int visit[maxn];
void Prime(){
    mem(visit,0);
    mem(prime, 0);
    for (int i = 2;i <= maxn; i++) {
        cout<<" i = "<<i<<endl;
        if (!visit[i]) {
            prime[++prime[0]] = i;      //纪录素数, 这个prime[0] 相当于 cnt,用来计数
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) {
//            cout<<"  j = "<<j<<" prime["<<j<<"]"<<" = "<<prime[j]<<" i*prime[j] = "<<i*prime[j]<<endl;
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
}



原理:
对于visit[i*prime[j]] = 1 的解释: 这里不是用i的倍数来消去合数,而是把 prime里面纪录的素数,升序来当做要消去合数的最小素因子。
发现 i 在消去合数中的作用是当做倍数的。而在上面的方法中,充当的是基数。
在这里插入图片描述

对于 i%prime[j] == 0 就break的解释 :当 i是prime[j]的倍数时,i = kprime[j],如果继续运算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],这里prime[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。

二、如果数组开不下,可以用改进版的试除法

首先看一个关于质数分布的规律:大于等于5的质数一定和6的倍数相邻。例如5和7,11和13,17和19等等;

证明:令x≥1,将大于等于5的自然数表示如下:

··· 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ···

可以看到,不和6的倍数相邻的数为6x+2,6x+3,6x+4,由于2(3x+1),3(2x+1),2(3x+2),所以它们一定不是素数,再除去6x本身,显然,素数要出现只可能出现在6x的相邻两侧。因此在5到sqrt(n)中每6个数只判断2个,时间复杂度O(sqrt(n)/3)。

在高配版和尊享版中,都是一个剪枝的思想,高配版中裁剪了不必要的偶数,尊享版中裁剪了不和6的倍数相邻的数,虽然都没有降低时间复杂度的阶数,但都一定程度上加快了判断的速度。

#include <iostream>
#include <math.h>
using namespace std;
int isPrime(int n)
{	//返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
	float n_sqrt;
	if(n==2 || n==3) return 1;
	if(n%6!=1 && n%6!=5) return 0;
	n_sqrt=floor(sqrt((float)n));
	for(int i=5;i<=n_sqrt;i+=6)
	{
	    if(n%(i)==0 | n%(i+2)==0) return 0;
	}
        return 1;
} 
int main()
{
	int flag;
	flag=isPrime(37);
	cout<<flag<<endl;
	return 0;
}
三、如果素数很大,可以用素数与素性测试(Miller-Rabin测试)

Miller-Rabin测试
https://www.cnblogs.com/Norlan/p/5350243.html

题目链接:
http://hihocoder.com/problemset/problem/1287

#include <iostream>
using namespace std ;
#define rd(x) (rand()%(x))
typedef unsigned long long ll;

ll pow_mod(ll a,ll b,ll r)
{
    ll ans=1,buff=a;
    while(b)
    {
        if(b&1)
            ans=(ans*buff)%r;
        buff=(buff*buff)%r;
        b>>=1;
    }
    return ans;
}

bool test(ll n,ll a,ll d)
{
    if(n==2) return true;
    if(n==a) return false;
    if(!(n&1)) return false;
    while(!(d&1)) d>>=1;
    ll t = pow_mod(a,d,n);
    while(d!=n-1&&t!=n-1&&t!=1){
        t = t*t%n;//下面介绍防止溢出的办法,对应数据量为10^18次方;
        d<<=1;
    }
    return t == n-1||(d&1)==1;//要么t能变成n-1,要么一开始t就等于1
}

bool isprime(ll n)
{
    int a[] = {2,3,5,7};//或者自己生成[2,N-1]以内的随机数rand()%(n-2)+2
    for(int i = 0; i <= 3; ++i){
        if(n==a[i]) return true;
        if(!test(n,a[i],n-1)) return false;
    }
    return true;
}
int main()
{
    int t,ans=0;
    ll N;  
    for(cin >> t;t;t--){
        cin >> N;
        cout << ((isprime(N))?"Yes":"No") <<endl;
    }
}
四、如果达到了大整数,就用java了,java里Big number自带了素数判定方法
import java.math.*;
import java.util.*;
public class prime {
	private static Scanner in;
 
	public static void main(String[] arge)
	{
		BigInteger n;
		in = new Scanner(System.in);
		while(in.hasNextBigInteger())
		{
			n=in.nextBigInteger();
			if(n.isProbablePrime(1))
			{
				System.out.println("Yes");
			}
			else
				System.out.println("No");
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值