素数判断方法

本文介绍了素数判断的多种方法,包括基本的逐个测试、利用平方根简化、基于孪生素数特性和普通筛选法的优化,展示了不同方法的时间复杂度和效率提升过程,以及一个实际应用实例——蓝桥杯Torry的困惑问题解决。
摘要由CSDN通过智能技术生成

素数(质数)判断方法

素数(质数)的判断在算法问题中经常遇到,这里小结几种常用的判断方法。

素数(质数)的定义

首先,我们来看一下素数(质数)的定义:质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。

方法一

我们可以从它的定义得到判断素数的 第一个方法: 从 2 到 n−1, 判断是否存在能被n整除的数,既(n%i==0,2<=i<=n−1),如果有就不是素数,否则为素数。

首先,可以先作一个小的优化,既除2以外,只需判断所有的奇数是否是素数。

//素数判断方法1
#include<stdio.h>
#include<time.h>
#define Max 1000000
int main()
{
    int sum = 1;//2单独处理,sum为素数的个数
    for(int i = 3; i <= Max; i+=2)
    {//因为偶数除了2之外都不是素数(质数),所以只需判断奇数,从3开始每次+2
        int j;
        for(j = 2; j < i; j++)//判断
            if(i%j == 0)break;
        if(j == i)
            sum++;
    }
    printf("Time used = %0.2f s\n",(double)clock()/CLOCKS_PER_SEC);//获取程序运行的时间
    printf("%d",sum);
    return 0;
}

程序的时间复杂度为O(n^2)。

结果表明n的数量级太大时,不宜采用该方法。 同时也可以得到结论1000,000中有78498个素数。

接下来的方法其实都是对上述方法进行优化,可以很好地降低时间复杂度。 利用以下结论:对正整数n,如果用2到 n√之间的所有整数去除,均无法整除,则n为质数。

方法二

//素数判断方法2
#include<stdio.h>
#include<time.h>
#include<math.h>
#define Max 1000000
int main()
{
    int sum = 1;
    for(int i = 3; i <= Max; i+=2)
    {//因为偶数除了2之外都不是素数(质数),所以只需判断奇数,从3开始每次+2
        int j;
        for(j = 2; j <= (int)sqrt(i); j++)//利用上述结论判断
            if(i%j == 0)break;
        if(j > (int)sqrt(i))
            sum++;
    }
    printf("Time used = %0.2f s\n",(double)clock()/CLOCKS_PER_SEC);
    printf("%d",sum);
    return 0;
}

程序的时间复杂度为O(n√)。

可以看到程序的运行时间已经大幅度地降低。

接下来的方法需要利用到孪生素数的一些特点和结论。

孪生素数

孪生素数:孪生素数指的是间隔为 2 的相邻素数。 1.当n>=6, n−1 和 n+1 为孪生素数,那么n 一定是6的倍数。 Proof :

∵n−1,n+1是素数,也即n−1和n+1是奇数∴n是偶数,n是2的倍数。设n不是3的倍数,即n=3k+1或n=3k+2。(i)当n=3k+1时,那么n−1=3k,已经与n−1是素数矛盾。(ii)当n=3k+2时,那么n+1=3(k+1),已经与n+1是素数矛盾。综上所述,n是3的倍数。∵n既是2的倍数,又是3的倍数∴n是6的倍数。

推论1 : 当 x>=1, (6x−1)或 (6x+1)不是素数时,它们的质因子不包括2和3的倍数,因为2(3x)−1,3(2x)−1,2(3x)+1,3(2x)+1。

2.素数的分布规律:当n>=5时,如果n为素数,那么n%6=1||n%6=5,即n一定出现在6x(x≥1)两侧。(就是说大于等于5的素数一定是分布在6倍数的左右两侧,但在6倍数左右两侧的数不一定是素数) Proof:

可以把6x附近的数用以下方式表示:……(6x−1),6x,6x+1,2(3x+1),3(2x+1),2(3x+2),6x+5,6(x+1)……不在6x两侧的数为:2(3x+1),3(2x+1),2(3x+2),它们不是素数,所以素数出现在6x的两侧。

  

有了以上的理论基础,我们可以对方法2进一步地优化,首先不在6x左右的数2,3单独处理,接下来只要判断6x两侧的数是否为素数。因为合数总是可以写成素数的乘积,那么我们直接用n去除以质数就可以达到很好地优化目的。而质数一定是 6x 两侧的数(推论一已证明了当n>=5时,n不是素数时,n 不含质因子2,3) , 6x 两侧的数是大于素数的集合,因此可以用 n 除以 6x 两侧的数即if(n % i == 0 || n % (i + 2) == 0)时,不是素数。

方法三

//素数判断方法3
#include<stdio.h>
#include<time.h>
#include<math.h>
#define Max 1000000
using namespace std;
int main()
{
    int sum = 1;//已经将2单独处理
    int flag = 0;
    for(int i = 3; i <= Max; i+=2)
    {
        if(i == 3)//3单独处理
            sum++;
         
        if(i % 6 != 1 && i % 6 !=5)
            continue;//不是6x两侧的数不是素数
        
        for(int j = 5; j <= (int)sqrt(i); j+=6)//对6x两侧的数进行判断
            if(i%j == 0 || i%(j + 2) ==0)
            {
                flag = 1;
                break;
            }
            if(flag == 1)
            {
                flag = 0;
                continue;
            }
        sum++;
    }
    printf("Time used = %0.2f s\n",(double)clock()/CLOCKS_PER_SEC);
    printf("%d",sum);
    return 0;
}

方法3运行结果表明比方法2快速了许多倍,已经是很高效的算法了。

方法四:普通筛选法

增加普通筛选法 比上面几种方法更快,也比较容易理解。 思想: 一个素数的倍数都不是素数。 时间复杂度: O(nloglogn)

#include<iostream>
#include<vector>
using namespace std;
const int maxt = 1000000;
vector<bool>prime;
int main()
{
    prime.resize(maxt,1);
    prime[0] = prime[1] = 0;//1是素数,0是非素数
    for(int i = 2; i*i <= prime.size(); i++)
    {
        if(prime[i] == 1)
        for(int j = i*2; j <= prime.size(); j += i)
        {
            prime[j] =  0;
        }
    }
    return 0;
}

相关习题:蓝桥杯 Torry的困惑(基本型)

#include<bits/stdc++.h>
using namespace std ;
​
bool isPrime(int m)
{
​
    bool flag=1 ;
    if(m%3==0) return 0 ; // 素数一定满足 6k+1 , 6k-1 
    for(int j=5;j<=(int)sqrt(m);j+=6)
        {
            if(m%j==0||m%(j+2)==0) 
                return 0 ;
        }
    return 1 ;
}
bool ishui(int m)
{
    if(m==11)   return 1 ;
    string s = to_string(m) ;
    int len=s.size() ;
    if(len%2==0) return 0 ;
    int i=0 ,j=len-1 ;
    bool flag=1 ;
    while(i<=j)
    {
        if(s[i]==s[j])
        {
            i++ ; j-- ;
        }
        else 
        {
            flag=0 ;
            break ;
        }
    }
    if(flag) return 1 ;
    else return 0 ;
}
int main()
{
    int a,b ;
    cin >> a >> b ;
    vector<int> arr ;
    if(a%2==0) a+=1 ;
    for(int i=a;i<=b;i+=2) //筛除所有偶数
    {
        if(ishui(i))
        {
            if(isPrime(i))
            arr.push_back(i) ;
        }
    }
    if(arr.size()==0) cout << -1 <<endl;
    else{
        for(int i=0;i<arr.size();i++)
            cout << arr[i]<<endl ;
    }
​
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值