素数的判断(大数据,大规模)
素数的判断其实谁都会,所以这篇跳过简单的素数判断,直接学习如何快速判断1到N的素数,以及判断大数据是否为素数。
(1)埃氏筛法
现在我们先学习埃氏筛选法,此法实用与大规模判断素数,比如1到N的素数有那些啊,等等等等。
这个算法流弊哦,与辗转相除法一样古老哇。
首先,将2到n范围内的所有整数写下来。其中最小的数字2是素数,将表中2的倍数都划去。表中剩余的最小数字是3,不能被更小的数整除,是素数。如果表中最小的是m,m为素数,将m的倍数划去。
附上表格学习:
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
2 | 3 | - | 5 | - | 7 | - | 9 | - | 11 | - |
2 | 3 | - | 5 | - | 7 | - | - | - | 11 | - |
#include<stdio.h> #define MAX 1000000 bool is[MAX];///判断 int pr[MAX];///储存素数 ///返回1到n有多少个素数,以及储存 int sieve(int n) { int p=0; ///初始化让他们高兴一下先 for(int i=0 ; i<=n ;i++) is[i]=true; is[0]=is[1]=false;///根本都不是自己人 for(int i=2 ; i<=n ; i++) { ///如果是自己人 if(is[i]) { ///我就记录咯 pr[++p]=i; ///那现在就很明显了,关于自己人I的倍数都不是自己人了 for(int j=i*2 ; j<=n ; j+=i) is[j]=false; } } return p; } int main() { int n; scanf("%d",&n); printf("%d\n",sieve(n)); return 0; }
往往题目不会那么简单,现在来搞一个难一点的区间筛法哼哼。。。。
求区间[a,b]内的素数个数,哇哈哈哈。
b以内的合数一定不会超过根号b,如果有根号b以内的素数表的话,那我们就可以把它运用到(a,b]上。也就是说我们要先搞出[ 2 ,根号b] 的表与[ a , b ]的表,然后在从[ 2 , 开B ]的表中筛选出素数的同时,也将其倍数在[ a , b ]的表中划去,流弊吧。
#include<stdio.h> #define MAX 1000007 #define ll long long bool issmall[MAX];///[2,sqrt(b] bool is[MAX];///[a,b] void seg(ll a,ll b) { ll i,j; ///对[2,sqrt(b))的初始化全为质数 for( i=0 ; i*i<b ; i++) issmall[i]=true; ///对下标偏移后的[a,b)进行初始化 for( i=0 ; i<b-a ; i++) is[i]=true; ///在筛选[2,sqrt(b)],在筛选这个区间的同时,将大区间的也搞了 for( i=2 ; i*i<b ; i++) { if(issmall[i]) { for( j=2*i ; j*j<b ; j+=i) issmall[i]=false;///不是自己人 ///(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选 for( j=(a+i-1)/i*i ; j<b ;j+=i ) is[j-a]=false; } } } int main() { ll a,b; while(scanf("%lld%lld",&a,&b)!=EOF) { seg(a,b); ll cnt=0; for(ll j=0 ; j<b-a ; j++) { if(is[j]) { cnt++; } } if(a==1) cnt--; printf("%d\n",cnt); } }
素不素很流弊勒,现在来实战下;
题意:给定两个素数n和m,要求把n变成m,每次变换时只能变一个数字,即变换后的数与变换前的数只有一个数字不同,并且要保证变换后的四位数也是素数。求最小的变换次数;如果不能完成变换,输出Impossible。
无论怎么变换,个位数字一定是奇数(个位数字为偶数肯定不是素数),这样枚举个位数字时只需枚举奇数就行;而且千位数字不能是0。所以可以用广搜,枚举各个数位上的数字,满足要求的数就加入队列,直到变换成功。因为是广搜,所以一定能保证次数最少。
AC代码:
#include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<algorithm> using namespace std; int n, m; const int N = 1e4 + 100; int vis[N]; struct node { int x, step; }; queue<node> Q; bool judge_prime(int x) //判断素数 { if(x == 0 || x == 1) return false; else if(x == 2 || x == 3) return true; else { for(int i = 2; i <= (int)sqrt(x); i++) if(x % i == 0) return false; return true; } } void BFS() { int X, STEP, i; while(!Q.empty()) { node tmp; tmp = Q.front(); Q.pop(); X = tmp.x; STEP = tmp.step; if(X == m) { printf("%d\n",STEP); return ; } for(i = 1; i <= 9; i += 2) //个位 { int s = X / 10 * 10 + i; if(s != X && !vis[s] && judge_prime(s)) { vis[s] = 1; node temp; temp.x = s; temp.step = STEP + 1; Q.push(temp); } } for(i = 0; i <= 9; i++) //十位 { int s = X / 100 * 100 + i * 10 + X % 10; if(s != X && !vis[s] && judge_prime(s)) { vis[s] = 1; node temp; temp.x = s; temp.step = STEP + 1; Q.push(temp); } } for(i = 0; i <= 9; i++) //百位 { int s = X / 1000 * 1000 + i * 100 + X % 100; if(s != X && !vis[s] && judge_prime(s)) { vis[s] = 1; node temp; temp.x = s; temp.step = STEP + 1; Q.push(temp); } } for(i = 1; i <= 9; i++) //千位 { int s = i * 1000 + X % 1000; if(s != X && !vis[s] && judge_prime(s)) { vis[s] = 1; node temp; temp.x = s; temp.step = STEP + 1; Q.push(temp); } } } printf("Impossible\n"); return ; } int main() { int t, i; scanf("%d",&t); while(t--) { while(!Q.empty()) Q.pop(); scanf("%d%d",&n,&m); memset(vis,0,sizeof(vis)); vis[n] = 1; node tmp; tmp.x = n; tmp.step = 0; Q.push(tmp); BFS(); } return 0; }
(2)埃氏的优化,依拉筛法
我们可以知道,任意一个正整数k,若k≥2,则k可以表示成若干个质数相乘的形式。Eratosthenes筛法中,在枚举k的每一个质因子时,我们都计算了一次k,从而造成了冗余。因此在改进算法中,只利用k的最小质因子去计算一次k。
与Eratosthenes筛法不同的是,对于外层枚举i,无论i是质数,还是是合数,我们都会用i的倍数去筛。但在枚举的时候,我们只枚举i的质数倍。比如2i,3i,5i,…,而不去枚举4i,6i…,原因我们后面会讲到。
此外,在从小到大依次枚举质数p来计算i的倍数时,我们还需要检查i是否能够整除p。若i能够整除p,则停止枚举。
利用该算法,可以保证每个合数只会被枚举到一次。
综上,Eular筛法可以保证每个合数只会被枚举到一次,时间复杂度为O(n)。当N越大时,其相对于Eratosthenes筛法的优势也就越明显。
AC代码:
int primelist[N], primecount = 0; bool isprime[N]; void eular(int n){ int i, j; for (i = 0; i <= n; i++){ isprime[i] = true; } isprime[0] = isprime[1] = false; for (i = 2; i <= n; i++){ if (isprime[i]){ primelist[primecount++] = i; } for (j = 0; j < primecount; j++){ if (i*primelist[j] > n){ break; } isprime[i*primelist[j]] = false; if (i%primelist[j]==0){ break; } } } }
(3)大数据打表法
#include<stdio.h> #include<math.h> int p[1000000],a[10000001],t=0; int prime(int n) { int i,q; q=(int)sqrt(n); for(i=0;p[i]<=q&&t;i++) if(n%p[i]==0)return 0; return 1; } void main() { int n,i; scanf("%d",&n); for(i=2;i<=n;i++) if(prime(i))p[t++]=i; for(i=0;i<t;i++) printf("%d%c",p[i],i<t-1?' ':'/n'); }
(4)神奇万能法
适合单数据,也适合超大数据
#include<stdio.h> #include<math.h> int p[8]={4,2,4,2,4,6,2,6}; int prime(int n) { int i=7,j,q; if(n==1)return 0; if(n==2||n==5||n==3)return 1; if(n%2==0||n%3==0||n%5==0)return 0; q=(int)sqrt(n); for(;i<=q;){ for(j=0;j<8;j++){ if(n%i==0)return 0; i+=p[j]; } if(n%i==0)return 0; } return 1; } void main() { int n; scanf("%d",&n); if(prime(n))puts("Yes"); else puts("No"); }
大素数测试+求最小素因子+最大素因子(模版)
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #define MAXN 10 #define C 16381 using namespace std; typedef __int64 I64; I64 min; I64 multi(I64 a, I64 b, I64 n){ I64 tmp = a % n, s = 0; while(b){ if(b & 1) s = (s + tmp) % n; tmp = (tmp + tmp) % n; b >>= 1; } return s; } I64 Pow(I64 a, I64 b, I64 n){ I64 tmp = a % n, s = 1; while(b){ if(b & 1) s = multi(s, tmp, n); tmp = multi(tmp, tmp, n); b >>= 1; } return s; } int witness(I64 a, I64 n){ I64 u = n - 1, t = 0, i, x, y; while(!(u & 1)) u >>= 1, t ++; x = Pow(a, u, n); for(i = 1; i <= t; i ++){ y = multi(x, x, n); if(y == 1 && x != 1 && x != n -1) return 1; x = y; } if(x != 1) return 1; return 0; } int test(I64 n){ I64 a; int i; if(n == 2) return 1; if(n < 2 || !(n & 1)) return 0; srand((I64)time(0)); for(i = 0; i < MAXN; i ++){ a = ((I64) rand()) % (n - 2) + 2; if(witness(a, n)) return 0; } return 1; } I64 gcd(I64 a, I64 b){ return b ? gcd(b, a % b) : a; } I64 pollard_rho(I64 n, I64 c){ I64 x, y, d, i = 1, k = 2; srand((I64)time(0)); x = ((I64) rand()) % (n - 1) + 1; y = x; while(1){ i ++; x = (multi(x, x, n) + c) % n; d = gcd(y - x + n, n); if(d != 1 && d != n) return d; if(y == x) return n; if(i == k) y = x, k <<= 1; } } void find(I64 n, I64 c){ I64 r; if(n <= 1) return; if(test(n)){ if(min > n) min = n; return; } r = pollard_rho(n, c--); find(n / r, c); find(r, c); } I64 MaxPrimeFactor(I64 n) { if(test(n)) return n; I64 k=-1,g; min=n; find(n,C); g=MaxPrimeFactor(min); k=g>k?g:k; g=MaxPrimeFactor(n/min); k=g>k?g:k; return k; } int main(){ I64 n; while(~scanf("%I64d", &n)) { // if(test(n)){ //test(n)测试n是不是素数 // printf("Prime\n"); // continue; // } // min = n; //min表示n的最小素因子 // find(n, C); //找出n的最小素因子 // printf("%I64d\n",min); printf("%I64d\n",MaxPrimeFactor(n));//求n的最大素因子 } return 0; }