素数判断-----埃氏筛法&欧拉筛法
埃氏筛法
/* |埃式筛法| |快速筛选素数| |15-7-26| */ #include <iostream> #include <cstdio> using namespace std; const int SIZE = 1e7; int prime[SIZE]; // 第i个素数 bool is_prime[SIZE]; //true表示i是素数 int slove(int n) { int p = 0; for(int i = 0; i <= n; i++) is_prime[i] = true; //初始化 is_prime[0] = is_prime[1] = false; //0,1不是素数 for(int i = 2; i <= n; i++) { if(is_prime[i]) //这里比较巧妙, 我只是意会 { prime[p++] = i; //计算素数的个数,也记录下了素数 for(int j = 2 * i; j <= n; j += i) // 除掉了i的倍数的数字 is_prime[j] = false; } } return p; } int main() { int n; while(cin >> n) { int res = slove(n); cout << res << endl; for(int i = 0; i < res; i++) cout << prime[i] << endl; } }
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
2 | 3 | - | 5 | - | 7 | - | 9 | - | 11 | - |
2 | 3 | - | 5 | - | 7 | - | - | - | 11 | - |
结合这张表看看,慢慢一次次的都筛选完了..
其中最小的素数是2,将表中所有2的倍数都除去,剩下最小的数是3,不能被更小的数整除,所以是素数.再将表中3的倍数的数除去.以此类推.如果表中最小的数字是m,m就是素数.然后将表中所有m的倍数都除去...然后就可以了= =
话说要是求区间[x,y]内求素数个数的话,只要0~y的素数个数-0~x的素数个数就可以了,然后判断x是否为素数就可以了...
欧拉函数
#include<cstdio> #include<cmath> using namespace std; #define m 5800000 #define m1 100000000 int n,len,a[m]; bool vis[m1]; int main() { scanf("%d",&n); for(int i = 2;i <= n;i++) { if(!vis[i]) a[++len] = i; for(int j = 1;(j <= len )&&(i * a[j] <= n);j++) { vis[a[j] * i] = true; if(!(i % a[j])) break; } } printf("%d",len); return 0; }
要说保证某数不被重复判断,关键就在这行代码上。
满足上式时,i是pri[j]的倍数,那么对于后面的pri[k]来说,i * pri[k]就可以被分解为pri[j] * (i / pri[j] *pri[k]),而该数在之前i == pri[j]的时候已经出现过,于是就重复了。直接退出,避免了重复判定。
欧拉筛法固然快,但是也有适用条件,例如求(n, n + 10)中的素数个数的时候,它就明显慢了。由于必须从1开始,所以欧拉筛法的用处在于直接打表而不是求某一段区间。对于这种问题,朴素法未尝不可。
欧拉筛法的原理分析至此结束。
------=====-----=====-----====-----======-------=========================================================
埃氏筛法:
从2开始把素数的倍数都给标记掉,复杂度为 O(nlog(logn)) 。
欧拉筛法:
把2压到栈/队列中,外面的数可以被栈/队列中的数整除的话,丢掉,不能的话,压入栈/队列中。
以6为例:
埃氏筛法会遍历到他很多遍
欧拉筛法只会遍历一遍