数论——素数


一、定义

素数(又称质数)是指在大于1自然数中,除了1和它本身以外不再有其他因数的自然数。
1 既非素数也非合数,2 是唯一偶素数

二、定理

π ( x ) 为1到x中素数的个数
由黎曼推导出下面的精确公式,详情见Riemann Prime Counting Function
在这里插入图片描述
然后又有一个神奇的比式极限
在这里插入图片描述
让我们得到如下结论:

  • 在自然数集中,小于n的质数有如下公式结果个
    n l n ( n ) \frac{n}{ln(n)} \quad ln(n)n
    (109 大约有48,254,942个素数(数量级为107))

  • 伯特兰-切比雪夫定理:若整数n > 3,则至少存在一个质数p,符合n < p < 2n − 2。另一个稍弱说法是:对于所有大于1的整数n,至少存在一个质数p,符合n < p < 2n。
    这个性质的应用:【XR-3】小道消息

三、素数判定

试除法

一个数如果不是素数,则一定能被一个小于它自己的数整除。
在这里插入图片描述

bool isprime(int x)
{
    if(x<2) return false;
    for(int i=2; i*i<=x; i++)
    {
        if(x % i == 0)
            return false;
    }
    return true;
}

预处理法

在这里插入图片描述
具体为后面的筛法。

kn+i 法

在这里插入图片描述

Miller-Rabin判定法

前言

MillerRabin素数测试是一种很实用的素数判定方法。
预处理法确实节省了不少时间,但是如果素数很大,那么为了判断一个素数就还得从头开始扫描,空间代价也不少,故不能接受。
而Miller-Rabin判定法只针对单个数字进行判定,因而可以对较大的乃至于long long范围内的数进行判定,而且速度也很快,是个十分优秀的算法。

前置知识

  1. 费马小定理:ap−1 ≡ 1(mod p) (p为素数)
  2. 二次探测定理:若p为奇素数且 x2 ≡ 1(mod p),则x ≡ 1(mod p)或x ≡ p−1(mod p)

算法流程

1)先判断0,1,2和所有偶数
2)设我们要测试的数为x,取一个质数a,设s,t满足2s · t = x-1 ( t为奇数)
3)算出at ,然后不断自乘并且进行二次探测检验(进行s次),不满足就为合数
4)进行s次后就得到ax-1 ,再判断 ax-1 ≡ 1 (mod x)是否成立,否就为合数
5)取多个不同的a,提高正确性

注:
这不是一个能保证正确的算法,但幸运的是我们可以通过多次执行算法降低错误的概率。
如果经过 t 轮测试,则p不是素数的概率为 1 4 t \frac {1}{4^t} 4t1
所以当 a 取遍小等于 30 的所有素数时,可以证明 int 范围内的数不会出错。
另外,如果是求一个 long long 类型的平方,可能会爆掉,因此有时我们要用“快速积”,不能直接乘。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int prime[10]={2,3,5,7,11,13,17,19,23,29};
int Quick_Multiply(int a,int b,int c)  //快速积(和快速幂差不多) 
{
    long long ans=0,res=a;
    while(b)
    {
        if(b&1)
          ans=(ans+res)%c;
        res=(res+res)%c;
        b>>=1;
    }
    return (int)ans;
}
int Quick_Power(int a,int b,int c)     //快速幂,这里就不赘述了 
{
    int ans=1,res=a;
    while(b)
    {
        if(b&1)
          ans=Quick_Multiply(ans,res,c);
        res=Quick_Multiply(res,res,c);
        b>>=1;
    }
    return ans;
}
bool Miller_Rabin(int x)     //判断素数 
{
    int i,j,k;
    int s=0,t=x-1;
    if(x==2)  return true;   //2是素数 
    if(x<2||!(x&1))  return false;     //如果x是偶数或者是0,1,那它不是素数 
    while(!(t&1))  //将x分解成(2^s)*t的样子 
    {
        s++;
        t>>=1;
    }
    for(i=0;i<10&&prime[i]<x;++i)      //随便选一个素数进行测试 
    {
        int a=prime[i];
        int b=Quick_Power(a,t,x);      //先算出a^t
        for(j=1;j<=s;++j)    //然后进行s次平方 
        {
            k=Quick_Multiply(b,b,x);   //求b的平方 
            if(k==1&&b!=1&&b!=x-1)     //用二次探测判断 
              return false;
            b=k;
        }
        if(b!=1)  return false;   //用费马小定律判断 
    }
    return true;   //如果进行多次测试都是对的,那么x就很有可能是素数 
}
int main()
{
    int x;
    scanf("%d",&x);
    if(Miller_Rabin(x))  printf("Yes");
    else  printf("No");
    return 0;
}

四、素数的筛法

  • 埃氏筛(Eratosthenes 筛法)
    思想:如果x是合数,那么他的倍数也一定是合数,那么标记完所有的合数,剩下的就是素数。
    在这里插入图片描述
    更严谨的证明
bool vis[N];
void eshai(int n)
{
    vis[1]=1;
    memset(vis,0,sizeof(vis));
    for(int i=2; i<=n; i++)
    {
        if(vis[i])  continue;
        for(int j=i; j<=n/i; j++)
        {
            vis[i*j]=1;
        }
    }
}
  • 线性筛(欧拉筛)
    欧拉筛的算法能够保证每个数只会被他的最小质因子筛一次,所以时间复杂度是O(n)。
    对于每一个合数x,他的最小质因子为y,在x/y时就能被筛出,因此每个合数一定可以被标记到。
    欧拉筛还能够得到了每个数的最小质因子。
int n,prime[N],cnt=0;
bool vis[N];
void getprime()
{
    for(int i=2; i<=n; i++)
    {
        if(!vis[i]) prime[++cnt] = i;
        for(int j=1; j<=cnt && i*prime[j] <= n; j++)
        {
            vis[i*prime[j]] = 1;
            if(i % prime[j] == 0)   break;
        }
    }
}

参考资料

《算法竞赛中的初等数论》(一)正文 0x00整除、0x10 整除相关(ACM / OI / MO)(十五万字符数论书)
夜深人静写算法(三)- 初等数论入门
初学MillerRabin素数测试
Miller-Rabin素数测试算法

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值