素数的判断(大数据,大规模)

素数的判断其实谁都会,所以这篇跳过简单的素数判断,直接学习如何快速判断1到N的素数,以及判断大数据是否为素数。

 (1)埃氏筛法

现在我们先学习埃氏筛选法,此法实用与大规模判断素数,比如1到N的素数有那些啊,等等等等。

这个算法流弊哦,与辗转相除法一样古老哇。

首先,将2到n范围内的所有整数写下来。其中最小的数字2是素数,将表中2的倍数都划去。表中剩余的最小数字是3,不能被更小的数整除,是素数。如果表中最小的是m,m为素数,将m的倍数划去。

 附上表格学习:

2 3 4 5 6 7 8 9 10 11 12
2 3 - 5 - 7 - 9 - 11 -
2 3 - 5 - 7 - - - 11 -

 

#include<stdio.h>
#define MAX 1000000
bool is[MAX];///判断
int pr[MAX];///储存素数
///返回1到n有多少个素数,以及储存
int sieve(int n)
{
    int p=0;
    ///初始化让他们高兴一下先
    for(int i=0 ; i<=n ;i++)
        is[i]=true;
        is[0]=is[1]=false;///根本都不是自己人
        for(int i=2 ; i<=n ; i++)
        {
            ///如果是自己人
            if(is[i])
            {
                ///我就记录咯
                pr[++p]=i;
                ///那现在就很明显了,关于自己人I的倍数都不是自己人了
                for(int j=i*2 ; j<=n ; j+=i)
                    is[j]=false;
            }
        }
        return p;
}
int main()
{
    int n;
    scanf("%d",&n);
    printf("%d\n",sieve(n));
    return 0;
}
View Code

往往题目不会那么简单,现在来搞一个难一点的区间筛法哼哼。。。。

求区间[a,b]内的素数个数,哇哈哈哈。

b以内的合数一定不会超过根号b,如果有根号b以内的素数表的话,那我们就可以把它运用到(a,b]上。也就是说我们要先搞出[ 2 ,根号b] 的表与[ a , b ]的表,然后在从[ 2 , 开B ]的表中筛选出素数的同时,也将其倍数在[ a , b ]的表中划去,流弊吧。

#include<stdio.h>
#define MAX 1000007
#define ll long long
bool issmall[MAX];///[2,sqrt(b]
bool is[MAX];///[a,b]
void seg(ll a,ll b)
{
    ll i,j;
    ///对[2,sqrt(b))的初始化全为质数
    for( i=0 ; i*i<b ; i++)
        issmall[i]=true;
    ///对下标偏移后的[a,b)进行初始化
    for( i=0 ; i<b-a ; i++)
        is[i]=true;
        ///在筛选[2,sqrt(b)],在筛选这个区间的同时,将大区间的也搞了
    for( i=2 ; i*i<b ; i++)
    {
        if(issmall[i])
        {
            for( j=2*i ; j*j<b ; j+=i)
                issmall[i]=false;///不是自己人
             ///(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选
            for( j=(a+i-1)/i*i ; j<b ;j+=i )
                is[j-a]=false;
        }
    }
}
int main()
{
    ll a,b;
    while(scanf("%lld%lld",&a,&b)!=EOF)
    {
        seg(a,b);
        ll cnt=0;
        for(ll j=0 ; j<b-a ; j++)
        {
            if(is[j])
            {
                cnt++;
            }
        }

        if(a==1)
            cnt--;
        printf("%d\n",cnt);
    }
}
View Code

素不素很流弊勒,现在来实战下;

题意:给定两个素数n和m,要求把n变成m,每次变换时只能变一个数字,即变换后的数与变换前的数只有一个数字不同,并且要保证变换后的四位数也是素数。求最小的变换次数;如果不能完成变换,输出Impossible。

无论怎么变换,个位数字一定是奇数(个位数字为偶数肯定不是素数),这样枚举个位数字时只需枚举奇数就行;而且千位数字不能是0。所以可以用广搜,枚举各个数位上的数字,满足要求的数就加入队列,直到变换成功。因为是广搜,所以一定能保证次数最少。

AC代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;

int n, m;
const int N = 1e4 + 100;
int vis[N];
struct node
{
    int x, step;
};
queue<node> Q;

bool judge_prime(int x) //判断素数
{
    if(x == 0 || x == 1)
        return false;
    else if(x == 2 || x == 3)
        return true;
    else
    {
        for(int i = 2; i <= (int)sqrt(x); i++)
            if(x % i == 0)
                return false;
        return true;
    }
}

void BFS()
{
    int X, STEP, i;
    while(!Q.empty())
    {
        node tmp;
        tmp = Q.front();
        Q.pop();
        X = tmp.x;
        STEP = tmp.step;
        if(X == m)
        {
            printf("%d\n",STEP);
            return ;
        }
        for(i = 1; i <= 9; i += 2) //个位
        {
            int s = X / 10 * 10 + i;
            if(s != X && !vis[s] && judge_prime(s))
            {
                vis[s] = 1;
                node temp;
                temp.x = s;
                temp.step = STEP + 1;
                Q.push(temp);
            }
        }
        for(i = 0; i <= 9; i++) //十位
        {
            int s = X / 100 * 100 + i * 10 + X % 10;
            if(s != X && !vis[s] && judge_prime(s))
            {
                vis[s] = 1;
                node temp;
                temp.x = s;
                temp.step = STEP + 1;
                Q.push(temp);
            }
        }
        for(i = 0; i <= 9; i++) //百位
        {
            int s = X / 1000 * 1000 + i * 100 + X % 100;
            if(s != X && !vis[s] && judge_prime(s))
            {
                vis[s] = 1;
                node temp;
                temp.x = s;
                temp.step = STEP + 1;
                Q.push(temp);
            }
        }
        for(i = 1; i <= 9; i++) //千位
        {
            int s = i * 1000 + X % 1000;
            if(s != X && !vis[s] && judge_prime(s))
            {
                vis[s] = 1;
                node temp;
                temp.x = s;
                temp.step = STEP + 1;
                Q.push(temp);
            }
        }
    }
    printf("Impossible\n");
    return ;
}

int main()
{
    int t, i;
    scanf("%d",&t);
    while(t--)
    {
        while(!Q.empty()) Q.pop();
        scanf("%d%d",&n,&m);
        memset(vis,0,sizeof(vis));
        vis[n] = 1;
        node tmp;
        tmp.x = n;
        tmp.step = 0;
        Q.push(tmp);
        BFS();
    }
    return 0;
}
View Code

(2)埃氏的优化,依拉筛法

我们可以知道,任意一个正整数k,若k≥2,则k可以表示成若干个质数相乘的形式。Eratosthenes筛法中,在枚举k的每一个质因子时,我们都计算了一次k,从而造成了冗余。因此在改进算法中,只利用k的最小质因子去计算一次k。 
与Eratosthenes筛法不同的是,对于外层枚举i,无论i是质数,还是是合数,我们都会用i的倍数去筛。但在枚举的时候,我们只枚举i的质数倍。比如2i,3i,5i,…,而不去枚举4i,6i…,原因我们后面会讲到。

此外,在从小到大依次枚举质数p来计算i的倍数时,我们还需要检查i是否能够整除p。若i能够整除p,则停止枚举。

利用该算法,可以保证每个合数只会被枚举到一次。

综上,Eular筛法可以保证每个合数只会被枚举到一次,时间复杂度为O(n)。当N越大时,其相对于Eratosthenes筛法的优势也就越明显。

AC代码:

int primelist[N], primecount = 0;
bool isprime[N];
void eular(int n){
    int i, j;
    for (i = 0; i <= n; i++){
        isprime[i] = true;
    }
    isprime[0] = isprime[1] = false;
    for (i = 2; i <= n; i++){
        if (isprime[i]){
            primelist[primecount++] = i;
        }
        for (j = 0; j < primecount; j++){
            if (i*primelist[j] > n){
                break;
            }
            isprime[i*primelist[j]] = false;
            if (i%primelist[j]==0){
                break;
            }
        }
    }
}
View Code

(3)大数据打表法

#include<stdio.h>
#include<math.h>
int p[1000000],a[10000001],t=0;
int prime(int n)
{
    int i,q;
    q=(int)sqrt(n);
    for(i=0;p[i]<=q&&t;i++)
        if(n%p[i]==0)return 0;
    return 1;
}
void main()
{
    int n,i;
    scanf("%d",&n);
    for(i=2;i<=n;i++)
        if(prime(i))p[t++]=i;
    for(i=0;i<t;i++)
        printf("%d%c",p[i],i<t-1?' ':'/n');
}
View Code

(4)神奇万能法

适合单数据,也适合超大数据

#include<stdio.h>
#include<math.h>
int p[8]={4,2,4,2,4,6,2,6};
int prime(int n)
{
    int i=7,j,q;
    if(n==1)return 0;
    if(n==2||n==5||n==3)return 1;
    if(n%2==0||n%3==0||n%5==0)return 0;
    q=(int)sqrt(n);
    for(;i<=q;){
        for(j=0;j<8;j++){
            if(n%i==0)return 0;
            i+=p[j];
        }
        if(n%i==0)return 0;
    }
    return 1;
}
void main()
{
    int n;
    scanf("%d",&n);
    if(prime(n))puts("Yes");
    else puts("No");
}
View Code

 
大素数测试+求最小素因子+最大素因子(模版)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define MAXN 10
#define C 16381
using namespace std;
typedef __int64 I64;
 
I64 min;
 
I64 multi(I64 a, I64 b, I64 n){
    I64 tmp = a % n, s = 0;
    while(b){
        if(b & 1) s = (s + tmp) % n;
        tmp = (tmp + tmp) % n;
        b >>= 1;
    }
    return s;
}
 
I64 Pow(I64 a, I64 b, I64 n){
    I64 tmp = a % n, s = 1;
    while(b){
        if(b & 1) s = multi(s, tmp, n);
        tmp = multi(tmp, tmp, n);
        b >>= 1;
    }
    return s;
}
 
int witness(I64 a, I64 n){
    I64 u = n - 1, t = 0, i, x, y;
    while(!(u & 1))    u >>= 1, t ++;
    x = Pow(a, u, n);
    for(i = 1; i <= t; i ++){
        y = multi(x, x, n);
        if(y == 1 && x != 1 && x != n -1) return 1;
        x = y;
    }
    if(x != 1) return 1;
    return 0;
}
 
int test(I64 n){
    I64 a;
    int i;
    if(n == 2) return 1;
    if(n < 2 || !(n & 1)) return 0;
    srand((I64)time(0));
    for(i = 0; i < MAXN; i ++){
        a = ((I64) rand()) % (n - 2) + 2;
        if(witness(a, n)) return 0;
    }
    return 1;
}
 
I64 gcd(I64 a, I64 b){
    return b ? gcd(b, a % b) : a;
}
 
I64 pollard_rho(I64 n, I64 c){
    I64 x, y, d, i = 1, k = 2;
    srand((I64)time(0));
    x = ((I64) rand()) % (n - 1) + 1;
    y = x;
    while(1){
        i ++;
        x = (multi(x, x, n) + c) % n;
        d = gcd(y - x + n, n);
        if(d != 1 && d != n) return d;
        if(y == x) return n;
        if(i == k) y = x, k <<= 1;
    }
}
 
void find(I64 n, I64 c){
    I64 r;
    if(n <= 1) return;
    if(test(n)){
        if(min > n) min = n;
        return;
    }
    r = pollard_rho(n, c--);
    find(n / r, c);
    find(r, c);
}
 
I64 MaxPrimeFactor(I64 n)
{
    if(test(n))
    return n;
    
    I64 k=-1,g;
    min=n;
    find(n,C);
    
    g=MaxPrimeFactor(min);
    k=g>k?g:k;
    
    g=MaxPrimeFactor(n/min);
    k=g>k?g:k;
    return k;
}
int main(){
    I64 n;
        while(~scanf("%I64d", &n))
        {
            // if(test(n)){                //test(n)测试n是不是素数
            // printf("Prime\n");
            // continue;
            // }
            // min = n;                   //min表示n的最小素因子
            // find(n, C);                //找出n的最小素因子
            // printf("%I64d\n",min);
            printf("%I64d\n",MaxPrimeFactor(n));//求n的最大素因子
        }
    return 0;
}
View Code

 

posted @ 2018-05-02 19:11  shuai_hui  阅读(622)  评论(0编辑  收藏  举报