【数论】质数

质数


一.定义

     质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。

     对于一个足够大的整数N,不超过N的质数大约有N / ln N 个。


二.质数的判定

试除法:

       若一个正整数 N 为合数,则存在一个能整出 N 的数 T ,其中2 <= T <= N

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

三.质数的筛选

Eratosthenes筛法

//Eratosthenes筛法 O(N log logN)
#include <bits/stdc++.h>

const int Max=10010;
int n;
int vis[Max];

using namespace std;
int main()
{
   scanf("%d",&n);
   for(int i=2;i<=n;i++)
   {
   	 if(vis[i]) continue;
   	 cout<<i<<" ";
   	 for(int j=i;j<=n/i;j++) vis[i*j]=1;
   }
   return 0;
}

看代码应该就能懂了。。。


线性筛

      简单说就是在生成一个需要标记的合数时,每次只向现有的数中乘上一个质因子,并且让它是这个合数的最小质因子。这相

当于让合数的质因子总小到大累计,即让 12 只有 3*2*2 一种产生方式,比上面Eratosthenes筛法更优秀。

//线性筛 O(N)
#include <bits/stdc++.h>

const int Max=10010;
int n,tot;
int v[Max],ans[Max];   // v 数组记录每个数的最小质因子

int main()
{
   scanf("%d",&n);
   for(int i=2;i<=n;i++)
   {
   	 if(!v[i])
   	 {
   	   ans[++tot]=i;
   	   v[i]=i;
   	 }
   	 for(int j=1;j<=tot;j++)
   	 {
   	   if(v[i] < ans[j] || ans[j] > n/i) break;
   	   v[i * ans[j]]=ans[j];
   	 }
   }

   for(int i=1;i<=tot;i++) printf("%d ",ans[i]);
   return 0;
}

四.质因子分解

基本定理      

       任何一个大于 1 的正整数都能唯一分解为有限个质数的乘积,可写作:

                              N = p1^c1 * p2^c2 * ...... pm^cm

       其中 ci 都是正整数, pi 都是质数,且满足 p1 <p2 < p3 < ...... < pm。


试除法

inline void divide(int n)
{
   m = 0;
   for(int i = 2;i <= sqrt(n);i++)
   {
   	 if(n % i == 0) //i是质数 
   	 {
   	   p[++m] = i,c[m] = 0;
   	   while(n % i == 0) n /= i,c[m]++;
   	 }
   }
   if(n > 1) //n是质数
   {
     p[++m] = n,c[m] = 1;
   }
   for(int i = 1;i <= n;i++) cout<<p[i]<<"^"<<c[i]<<"\n";
}

【例题】Prime Distance POJ2689

       给定两个整数L,R(1 <= L < R <=2^31 , R-L <= 10^6),求闭区间[L,R]中相邻两个质数的差最大和最小是多少,分别

输出这两个数。


解析:

      首先暴力求出[1,R]中所有质数是不可能的,但是R-L得范围很小,并且任何一个合数必定包含一个不超过N 的质因子。

      所以我们只需要筛出2 —— R 之间的所有质数(可预处理)。对于每个质数 p ,把[L,R]中能被 p 整除的数标记为合数,

未被标记的为质数,再两两比较即可。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int Max=1000100;
int l,r,tot,sum;
int min1,min2,max1,max2;
int num[Max],v[Max],ans[Max];

inline void pre()    //预处理 
{
   memset(v,1,sizeof(v));
   for(int i=2;i<=46340;i++)
   {
   	 if(v[i])
   	 {
   	   num[++tot]=i;
   	   for(int j=2;j<=46340/i;j++) v[i*j]=0; //√2^31 ≈46340
   	 }
   }
}

int main()
{
   pre();
   while(scanf("%d%d",&l,&r)!=EOF)
   {
   	 memset(v,1,sizeof(v));
   	 if (l == 1) l=2;    //l = 1 时需特判 
   	 for(register int i=1;i<=tot;i++)
   	   for(register int j=l/num[i];j<=r/num[i];j++)
   	     if(j>1) v[num[i] * j - l]=0;
		sum = 0;
   	   for(int i=l;i<=r;i++)
   	   {
   	     if(v[i-l]) ans[++sum]=i;
   	     if(i==r) break;     //一定要有这个判断!不然 i 可能会达倒 int 最大值然后爆掉。。。
       }
		
   	 int t1=2147483647,t2=0;
   	 for(int i=2;i<=sum;i++)
   	 {
   	   if(ans[i]-ans[i-1] > t2)
	   {
	     t2=ans[i]-ans[i-1];
	     max1=ans[i-1];
	     max2=ans[i];
	   }
	   if(ans[i]-ans[i-1] < t1)
	   {
	     t1=ans[i]-ans[i-1];
	     min1=ans[i-1];
	     min2=ans[i];
	   }
     }
     if(!t2) printf("There are no adjacent primes.\n");
     else printf("%d,%d are closest, %d,%d are most distant.\n",min1,min2,max1,max2);
   }
   return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_38083668/article/details/81054753