欧拉函数问题

来源:互联网 发布:ip2780清零软件 编辑:程序博客网 时间:2024/06/05 16:19

对于这题,做过好久了,但是重新翻出来的时候,脑袋又一脸懵逼了,看不懂自己的代码,自己默默的琢磨了一个晚上,还问了朋友。

题意:求解n以内与n不互质的数的个数

现在我来梳理下我那个晚上到底发生了什么。。。。。再见

刚开始,看到这题,我想的是,求解不互质个数=n-互质的个数

那这题就转换成求解互质的个数。

无奈当时数学不好啊,好气啊。抓狂

求解互质那第一步要先求出n的质因数,这个好办,我素数筛选下用数组存起来就好了。

然后求解互质个数,我暴力for一下,枚举n以内的数能不能整除这些素数

以下是这个思路的代码:

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<vector>#include<math.h>#define N 32770using namespace std;typedef long long LL;bool prime[N];int isprime[N],num=0;vector<int> ans;void getprime(){    long long i,j,t;    for(i=2;i<=N-5;i++)    {        if(!prime[i])        {            isprime[++num]=i;            for(j=2;(t=i*j)<=N-5;j++)            {                prime[t]=true;            }        }    }}int main(){    getprime();    int T;    scanf("%d", &T);    while(T--){        ans.clear();        int n;        scanf("%d", &n);        for(int i=1; i<=num; i++){            int x = isprime[i];            if(x>n)                break;            if(n%x==0)                ans.push_back(x);        }        int answer=0;        for(int i=1; i<n; i++){            bool flag=false;            for(int j=0; j<(int)ans.size(); j++){                int x=ans[j];                if(i%x==0){                    flag=true;                    break;                }            }            if(!flag)                answer++;        }        printf("%d\n", answer);    }}

然而这个出现了一个问题,我两层for的时间复杂度极高,这样会不会超时呢?有什么更好的办法呢?
然后我去苦坑数学,我翻,我翻,我问,我问。。。
最后我知道了这个问题是欧拉函数。
是我们伟大的欧拉发现的!


对正整数n,欧拉函数是小于等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名(Euler'so totientfunction),它又称为Euler's totient functionφ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。

 
通式: φ(N)=N*(1-1/P1)*(1-1/P2)*...*(1-1/P)
其中p1, p2……pm为N的所有质因数,N是不为0的整数。


而我们从整数分解那里已经知道一个整数n可以分解为N=p1^a1*p2^a2……pm^am


代入得,φ(N)=p1^(a1-1)*(p1-1)*p2^(a2-1)*(p2-1)……pm^(am-1)*(pm-1)


还不懂的可以去看看这个:http://baike.baidu.com/link?url=IsY7joVqDMTYl1iHfJpiRMI0x-opH0ALhO3SFhxvXl07kS37wDvBnhnB52Gaj232sVhjYSD5wvyjdj8QjPfnW4Wuo3wkikHN7e3q35yTDOaTqeQcGJNnk6cwrPhPBMl9


那么,问题自然而然简单了,我们只要求出它分解之后的底数以及幂就行了,哈哈

下面有两种写法,你们自己看有啥区别吧~

1、正儿八经的写法

#include <iostream>   #include <cmath>   using namespace std;   #define MAXI 20   struct yinshu   {       int di;       int mi;   };   struct Div   {       int xiangshu;       int xdi[MAXI];       int xmi[MAXI];   };   Div getpN(int m);   void vout(Div pN);   yinshu getOne(int yin,int& m);   int main()   {       int ncase,n,i;       cin>>ncase;       Div pN;       while(ncase--)       {           cin>>n;           pN=getpN(n);           vout(pN);       }       return 0;   }   Div getpN(int m)   {       int i,j;       Div pans;       yinshu x;       if(m<2)       {           pans.xdi[0]=1;           pans.xmi[0]=0;           pans.xiangshu=0;           return pans;       }          i=2;       j=0;       for(i=2;i*i<=m;i++)       {             if(m%i==0)           {               x=getOne(i,m);               pans.xdi[j]=x.di;               pans.xmi[j]=x.mi;               j++;           }       }       if(m>1)       {           pans.xdi[j]=m;           pans.xmi[j]=1;           j++;       }       pans.xiangshu=j;       return pans;   }   void vout(Div pN)   {       int i,j;       int ans=1;    for(i=0;i<pN.xiangshu;i++)       {           for(j=1;j<=pN.xmi[i]-1;j++)        {           ans*=pN.xdi[i];        }        ans*=(pN.xdi[i]-1);    }       cout<<ans<<endl;   }   yinshu getOne(int yin,int& m)   {       yinshu x;       x.di=yin;       x.mi=0;       while(m%yin==0)       {           x.mi++;           m/=yin;       }       return x;   }  


2、 思路大概一样啊,其实我也不是很清楚闭嘴当时就这么做的,到现在也没怎么看懂,

     如果你们看懂了好心人可以在评论区告诉我,thank you !


#include <iostream>  using namespace std;  #define MAXI 20  int fix(int n); int Solve(int n);int main() {     int T,n;     cin>>T;     while(T--){         cin>>n;         cout<<n-Solve(n)<<endl;     }     return 0; }int fix(int n){     int ans=n;     int i;    for(i=2;i*i<=n;i++){         if(n%i==0)        {             ans=ans/i*(i-1);             while(n%i==0)             {                  n/=i;             }                        }     }     if(n>1)         ans=ans/n*(n-1);     return ans; } int Solve(int n){     int cnt=0;int i;     for(i=1;i*i<=n;i++){          if(n%i==0){              if(i>=2)                 cnt+=fix(n/i);             if((n/i)!=i && (n/i)>=2)                  cnt+=fix(i);         }     }     return cnt; }  




0 0