【素数】

1.欧拉函数

         在数论中,对正整数n,欧拉函数是小于或者等于n的数中与n互质的数的个数.假设n的唯一分解式为,根据容斥原理可知

                                          

对于{p1,p2,....,pk}的任意子集S,“不与其中任何一个互述素”的元素个数为。不过这一项的前面是加号还是减号呢?取决于S中元素的个数-———奇数个数就是"减号”,偶数个数就是“加号”,如果对这个地方有疑问的,可以参考下组合数学容斥原理的章节.

          现在我们得到了计算欧拉函数的公式,不过这样计算起来非常麻烦。如果根据公式直接计算,最坏情况下需要计算项的多项式。不过这点倒不用我们担心,前人已经在此公式上面已经做了相应的研究,这里直接给出公式的变形,上述公式可以变形成如下的公式:

                                       

           从而我们计算某个数的欧拉函数,只需要O(K)的计算时间,在刚才原始的基础上大大提高了效率。如果题目中没有给出唯一分解式,我们可以根据第二个小节的做法,在的时间复杂度解决这个问题.

  1. int euler(int n)  
  2. {  
  3.     const int border = sqrt(n);  
  4.     int cnt = n;  
  5.     for (int i = 2; i <= border; ++i)  
  6.     {  
  7.         if (n % i == 0)  
  8.         {  
  9.             cnt = cnt / i * (i - 1);  
  10.             while (n % i == 0)  
  11.                 n /= i;  
  12.         }  
  13.     }  
  14.     if (n > 1)  
  15.         cnt = cnt / n * (n - 1);  
  16.     return cnt;  
  17. }  
2.埃氏筛法

            上面介绍了一些关于素数和欧拉函数的小知识点,那现在进入主题——如何在O(N)的时间复杂度内求出某段范围的素数表.在ACM比赛中,有些题目往往需要求出某段范围内素数,而此时如何高效的求出素数表就显得尤为重要。关于素数表的求法,比较出名的是埃氏素数筛选法。其基本原理是每找到一个素数,将其倍数的数从素数表中删除,不断重复此过程,最终表中所剩数据全部为素数。下面的gif图片展示了该方法的相应步骤:



           埃氏素数筛选法的写法有多种版本,其时间复杂度为,这里给出一份实现代码.

  1. const int N = 1e+6 + 7;  
  2. bool prime[N];  
  3. void init_prime_table(int n)  
  4. {  
  5.     const int border = sqrt(n);  
  6.     memset(prime, truesizeof(prime));  
  7.     prime[0] = prime[1] = false;  
  8.     for (int i = 2; i <= border; ++i)  
  9.     {  
  10.         if (!prime[i])  
  11.             continue;  
  12.         //此处j值需要注意溢出的bug  
  13.         for (long long j = i * i; j <= n; j += i)  
  14.             prime[j] = false;  
  15.     }  
  16. }  
  3.欧拉筛法

        一般情况下,对于大部分的题目上面的写法已经够用了.然而,有人将上述的方法优化到了,效率虽然没有很大数量级的提升,不过,思想还是值得学习的.学过数学知识的人大都知道,对于一个正整数,如果其为合数,那么该数的质因数分解形式是唯一的。假设一个合数n的质因数分解形式为:

                                

             现定义:对于某个范围内的任意合数,只能由其最小的质因子将其从表中删除。我们很容易得出该算法的时间复杂度为线性的,为什么呢?因为一个合数的质因数分解式是唯一的,而且我们定义了合数只能由最小质因子将其从表中删除,所以每个合数只进行了一次删除操作(需要注意的是:埃氏素数筛选法中合数可能被多个质数删除,比如12,18等合数).现在原始的问题转换为怎么将合数由其最小的质因子删除?我们考查任何一个数n,假设其最小质因子为m,那么小于等于m的质数与n相乘,会得到一个更大的合数,且其最小质因数为与n相乘的那个质数,而该合数可以直接从表中删除,因为其刚好满足之前的合数删除的定义,所以我们需要维护一个表用来记录已经找到了的质数,然后根据刚才叙述的步骤执行,就能将埃氏素数筛选法的时间复杂度降为.

  1. const int N = 1e+6 + 7;  
  2. bool prime[N];  
  3. int rec[N], cnt;  
  4. void init_prime_table(int n)  
  5. {  
  6.     cnt = 0;  
  7.     memset(prime, truesizeof(prime));  
  8.     prime[0] = prime[1] = false;  
  9.     for (int i = 2; i <= n; ++i)  
  10.     {  
  11.         if (prime[i])  
  12.             rec[cnt++] = i;  
  13.         //此处边界判断为rec[j] <= n / i,如果写成i * rec[j] <= n,需要确保i * rec[j]不会溢出int  
  14.         for (int j = 0; j < cnt && rec[j] <= n / i; ++j)  
  15.         {  
  16.             prime[i * rec[j]] = false;  
  17.             if (i % rec[j] == 0)  
  18.                 break;  
  19.         }  
  20.     }  
  21. }  
         同样的,通过此种方法,我们可以在线性的时间生成某段范围的欧拉函数表,原理与上述类似,这里就不做过多的解释了。

4.Miller Rabbin(判断大素数)

费马小定理:

a为整数,n是素数,且a,n互质,则有a^(n-1)≡1(mod n) ,即:a^(n-1)模n得1。

快速判定一个数是否为素数的方法:

如果存在一个整数a,使得a^(n-1)≡1(mod n) ,则称n为基于a的伪素数,当有多个满足关系的a时,则n为素数的概率趋向于1。所以取多个a测试一下即可。


# include<iostream>
# include<cstdio>
# include<cstring>
# include<cstdlib>
# include<algorithm>
using namespace std;
# define ll long long
ll mypow(ll a,ll b,ll m)
{
    if(b==0)
        return 1;
    if(b==1)
        return a%m;
    ll temp=mypow(a,b/2,m);
    temp*=temp;
    temp%=m;
    if(b&1)
        temp*=a;
    temp%=m;
    return temp;
}
bool Miller_Rabbin(ll x)
{
    if(x==2)
        return true;        ///2要直接判断
    for(int i=1;i<=50;++i){
        ll a=rand()%(x-2)+2;
        if(mypow(a,x-1,x)!=1)
            return false;
    }
    return true;
}
int main()
{
    ll n;
    while(scanf("%lld",&n)!=EOF)
    {
        if(Miller_Rabbin(n))
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值