数论——终结素数判定

 

1.基础版(暴力)素数判定:

bool prime(int n) 
{
    if (n<=1)
        return false;
    int tmp=sqrt(n);
    for (int i=2;i<=tmp;i++)
        if (n%i==0)
            return false;
    return true;
}

 

2.再诉说一个素数规律。

 

//任何一个数都可以写成  6n,6n+1,6n+2,6n+3,6n+4,6n+5这种格式。

//显然6n , 6n+2 , 6n+4   能被2整除  
//      6n+3               能被3整除     
//因此满足上述格式的都为合数。

//只有满足  6n+1或6n+5 才有可能是素数。
//同时6n+5可以看成6n-1的另一种形式。
//即素数满足   6n-1和6n+1的形式 (除特例:2和3) 
//即结论:一个数若是素数,则他一定在6倍数的两边(除特例:2和3) 

 

  故可以将上述代码再进行优化剪枝。

bool prime(int n)  
{
    if (n<=1)   //1即不是素数,又不是合数 
        return false;
    else if (n==2||n==3)
        return true;
    else if (n%6!=1&&n%6!=5)
        return false;
    int tmp=sqrt(n);
    for (int i=5;i<=tmp;i+=6)
        if (n%i==0||n%(i+2))
            return false;
    return true;
}

 

3.有的时候,面临的问题是询问次数过多,这里就需要通过一定的预处理,将询问的复杂度降为O(1)。

  显然这里的预处理就是    线性素数筛

 

 

原理:n为素数,那么 i * n (i为非1的任意自然数)是非素数,即将 i * n 筛去。

 

原理很简单,但是我们面临的优化是如何将一个数尽可能的只筛一次。  看下图操作

 

 

如下图模拟线性素数筛步骤    :

 

 

 

代码:

 

const int MAX=10000010;   //线性素数筛的数据极限为1e7,大于这极限,则需要另寻他路 
bool is_prime[MAX+10];
int prime[MAX+10];
int cnt=0;
void init()
{
    is_prime[1]=true;
    
    for (int i=2;i<=10000010;i++){
        if (is_prime[i]==false)  
            prime[++cnt]=i;
        for (int k=1;k<=cnt&&i*prime[k]<=10000010;k++){
            is_prime[i*prime[k]]=true;
            if (i%prime[k]==0)
                break;
        }
    }
}

 

 

4.当我面临的数据超过了线性素数筛极限(1e7)的时候,那我如何处理?

 

  答案:再引进一个算法  Miller-Rabbin素数   ,    这是一个logn级别的算法  , 这是一个开挂的算法。

 

 

//      在学习该算法之前,应当了解    费马小定理。  

// 费马小定理:简单言之:   

// 假设p的素数,   那么 pow(a,p-1)≡1(%p) ,即pow(a,p-1)%p = 1


//那么如果有此式成立,是否p一定为质数?答案是否定的。但是,我们可以多测试几次,
//即随机选取a值进行测试,提高准确率。测试次数越大,正确率越高。(有一点随机数味道,一般a的取值次数在30次左右就能判定)

//优点:  算法数据极限可扩展到   int64 ,有点强。

//缺点:  测试一次失败的概率为  1/4,   测试30次失败的概率为    (1/4)^30。   即并非正确率100% 

 

posted @ 2019-08-03 15:35  生活待我如初恋  阅读(298)  评论(0编辑  收藏  举报