数论之素数及其相关定理
一、素数定义
基本概念:素数又称质数,是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
唯一分解定理:一个数n肯定能被分解成 n=p1^a1 * p2^a2 . . .*pn^an(p是素因子,a是素因子的个数)
因为一个数肯定是由合数和质数构成的,合数又可以分解成质数和合数,最后递归下去就会变成质数的乘积
最后化成了质数相乘的形式
二、素数的判定
朴素算法(时间复杂度O(n)):
bool isprime(int x){
for(int i=2;i<=x;i++){
if(x%i==0) return false;
}
return true;
}
进阶算法(时间复杂度O(sqrt(n))):
可以注意到如果在2~n-1中,存在n的约数,不妨设为k,即n%k==0,那么由k*(n/k)==n可知,n/k也是n的一个约数,且k与n/k中一定满足其中一个小于等于sqrt(n)、另一个大于等于sqrt(n),其中sqrt(n)为根号n。那么只需要判定n能否2,3…sqrt(n)中的一个整除,即可判定n是否为素数。
bool isprime(int n){
if(n<=1) return false;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0) return false;
}
return true;
}
三、筛法求素数
1、埃氏筛法(时间复杂度O(nloglogn))
素数筛法的关键就在于一个“筛”字。算法从小到大枚举所有数,对每一个素数,筛去它的所有倍数,剩下的即为素数。
const int maxn=1e4+10;
int prime[maxn],pnum=0;//prime数组存放所有素数,pnum为素数个数
bool p[maxn];//如果i为素数,则p[i]为false,否则p[i]为true
void find_prime(){
for(int i=2;i<maxn;i++){
if(p[i]==false){
prime[pnum++]=i;
for(int j=i+i;j<maxn;j+=i){
//筛去i的倍数
p[j]=true;
}
}
}
}
2.欧式筛法(时间复杂度O(n))
欧式筛法对于每个数字,总是被它的最小质因子筛掉,所以每个数字都只遍历一次,所以时间复杂度是O(n)。
首先,我们要保证每一个数字是被他的最小质因子筛掉的,那么我们遍历因子,用已经得到的素因子从小到大的去乘因子。
此时会出现两种情况:1、因子中不包含素因子,那么可以继续乘下去。2、因子中包含了素因子,那么如果因子继续乘下去,所得的积的最小质因子就是之前所包含的素因子,会出现重复筛除,所以break。
int prime[maxn],tot;
bool vis[maxn];
void isprime(){
for(int i=2;i<maxn;i++){
if(!vis[i]){
prime[tot++]=i;
}
for(int j=0;j<tot;j++){
if(i*prime[j]>maxn) break;//判断是否越界
vis[i*prime[j]]=1;//筛掉合数
if(i%prime[j]==0) break;//判断因子是否包含素因子
}
}
}
四、欧拉函数
1、欧拉函数定义
定义:在数论,对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目(因此φ(1)=1)。此函数以其首名研究者欧拉命名(Euler’s totient function),它又称为Euler’s totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。欧拉函数是积性函数,当 gcd(a,b)=1时,
通项公式:
p1,p2…pn为x的质因子
2、欧拉函数算法
int oula(int n){
int rea=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
rea=rea - rea/i;
while(n%i==0){
n/=i;
}
}
}
if(n>1) rea=rea-rea/n;
return rea;
}
3、欧拉打表
1.埃氏筛法打表
void getPhi(){
for(int i=1;i<maxn;i++){
p[i]=i;
}
for(int i=2;i<maxn;i++){
if(p[i]==i){
for(int j=i;j<maxn;j+=i){
p[j]=(p[j]/i)*(i-1);
}
}
}
}
2.欧氏筛法打表
void getPhi(){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!flag[i]){
prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot;j++){
if(i*prime[j]>n) break;
flag[i*prime[j]]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
}
}