(转载)O(N)的素数筛选法和欧拉函数

Posted Neord

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(转载)O(N)的素数筛选法和欧拉函数相关的知识,希望对你有一定的参考价值。

转自:http://blog.csdn.net/dream_you_to_life/article/details/43883367 作者:Sky丶Memory

1.一个数是否为质数的判定.

        质数,只有1和其本身才是其约数,所以我们判定一个数是否为质数,只需要判定2~(N - 1)中是否存在其约数即可,此种方法的时间复杂度为O(N),随着N的增加,效率依然很慢。这里有个O(技术分享)的方法:对于一个合数,其必用一个约数(除1外)小于等于其平方根(可用反证法证明),所以我们只需要判断2~技术分享之间的数即可.

 

1 bool is_prime(int num)  
2 {  
3     const int border = sqrt(num);  
4     for (int i = 2; i <= border; ++i)  
5         if (num % i == 0)  
6             return false;  
7     return 1 != num;  
8 }  

 


2.一个数的质因数分解

 

        对于一个数N的质因数分解,简单一点的方法通过枚举2~N之间的每个数字,如果N值能整除当前枚举的数,则将N值除尽,重复上面的步骤,直到结束.我们可以看出此种方法的时间复杂度为O(N),而我们通过上面介绍的方法,可以将时间复杂度降为O(技术分享),原理与判定一个数是否为质数是一样的.

 

 1 map<int, int> factor(int num)  
 2 {  
 3     map<int, int> ans;  
 4     const int border = sqrt(num);  
 5     for (int i = 2; i <= border; ++i)  
 6         while (num % i == 0)  
 7             ++ans[i], num /= i;  
 8     if (num > 1)  
 9         ans[num] = 1;  
10     return ans;  
11 }   

 

3.欧拉函数

         在数论中,对正整数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 }  

 

 

 

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

技术分享 

O(N)的素数筛选法和欧拉函数

 

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

 

 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, true, sizeof(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 }  

 

 

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

 

         同样的,通过此种方法,我们可以在线性的时间生成某段范围的欧拉函数表,原理与上述类似,这里就不做过多的解释了。

 

 

 

 

 


以上是关于(转载)O(N)的素数筛选法和欧拉函数的主要内容,如果未能解决你的问题,请参考以下文章

欧拉函数o(n)求素数

线性筛素数欧拉函数

线性筛素数(欧拉筛)

线性筛素数(欧拉筛)

线性筛素数(欧拉筛)

O(n)求素数,求欧拉函数,求莫比乌斯函数,求对mod的逆元,各种求