欧拉函数问题

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

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

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

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

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

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

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

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

以下是这个思路的代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<math.h>
#define N 32770
using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值