您的位置:首页 > 其它

利用欧拉函数解决最大公约数相关问题

2013-07-11 12:24 525 查看
一、欧拉函数与最大公约数

欧拉函数phi(i)表示在[1~i)中,与i互质的数的个数。

如要求1-n中所有数与n的最大公约数的和。

设count(x)为1-n中,与n的最大公约数为x的个数,即gcb(i,n)=x,i的个数。(gcb(x,y)为x和y的最大公约数)

这个count(x)怎么求呢?

有gcb(i,n)=x,则gcb(i/x,n/x)=1,(如果为k,k!=1,则gcb(i,n)=k*x)。

则count(x)=phi(n/x)。因为这些数既然与n/x互质,则将这些数乘以x,则得到的数肯定与n最大公约数为x。

这样,我们就将n以内与n互质的数的个数(欧拉函数),转换为与n最大公约数为x的个数。


二、欧拉函数与代码

在知道了欧拉函数这么强大之后,我们来看看怎么求它。

维基百科上:








例如 


根据上面的公式,我们可以得到如下代码:
int euler(int n)
{
int i,m;
m = n;
for(i=2; i<(int)sqrt(n*1.0)+1; i++)
{
if(n%i == 0)
{
m = m/i*(i-1); //找到一个素数,也可以用m = m - m/i;即m = m*(1-1/i);
while(n%i == 0) //保证上一步找到的都是素数
n /= i;
}
}
if(n>1)
m = m/n*(n-1);
return m;
}

或者这样:
#define maxn 1000
int phi[maxn];

void euler()
{
int i,j;
for(i=1;i<maxn;i++) phi[i]=i;
for(i=2;i<maxn;i++)
if(phi[i]==i) //判断是否为素数
for(j=i;j<maxn;j+=i)
phi[j]=phi[j]/i*(i-1); //对每个数组元素,处理掉i这个素数
}

欧拉函数是积性函数,即是说若m,n互质,


欧拉定律:

对任何两个互质的正整数a, m(即 gcd(a,m) = 1),

,有



当m是质数p时,此式则为:



费马小定理

三、相关问题

1、给出一个n(n<=10^9),求1-n这n个数,同n的最大公约数的和。比如:n = 6

      1,2,3,4,5,6 同6的最大公约数分别为1,2,3,2,1,6,加在一起 = 15


     根据第一节的分析,可以直接得到如下代码(从网上copy的):

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
#define N 1000005

typedef long long LL;

bool prime
;
LL p
;
LL k=0;

void isprime()
{
LL i,j;
memset(prime,true,sizeof(prime));
for(i=2;i<N;i++)
{
if(prime[i])
{
p[k++]=i;
for(j=i+i;j<N;j+=i)
{
prime[j]=false;
}
}
}
}

LL phi(LL n)
{
LL rea=n,i;
for(i=0;p[i]*p[i]<=n;i++)
{
if(n%p[i]==0)
{
rea=rea-rea/p[i];
while(n%p[i]==0) n/=p[i];
}
}
if(n>1)
rea=rea-rea/n;
return rea;
}

int main()
{
LL i,n,ans;
isprime();
while(cin>>n)
{
ans=0;
for(i=1;i*i<=n;i++)
{
if(i*i==n)
ans+=i*phi(n/i);
else if(n%i==0)
ans+=i*phi(n/i)+(n/i)*phi(i);
}
cout<<ans<<endl;
}
return 0;
}
2、求1-n中所有数的最大公约数之

网上找到的凉粉代码,

代码1:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#define maxn 1000005
#define LL long long

int phi[maxn];
LL sumphi[maxn];

void Init()
{
int i,j;
memset(sumphi,0,sizeof(sumphi));
for(i=1;i<maxn;i++) phi[i]=i;
for(i=2;i<maxn;i++)
if(phi[i]==i)
for(j=i;j<maxn;j+=i)
phi[j]=phi[j]/i*(i-1);
for(i=1;i<maxn;i++)
sumphi[i]=sumphi[i-1]+phi[i];
}

int main()
{
Init();
int t,n,i,j;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
LL G=0;
for(i=1;i<=n;i++)
G+=i*(sumphi[n/i]-1); //计算每个最大公约数d出现的次数
printf("%lld\n",G);
}
return 0;
}


代码2:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#define maxn 1000005
#define LL __int64

int phi[maxn];
LL ans[maxn]; //ans[x]=sum(ans[i]*j); (i*j=x)
void Init()
{
int i,j,k;
for(i=2;i<maxn;i++) phi[i]=i;
for(i=2;i<maxn;i++)
if(phi[i]==i)
for(j=i;j<maxn;j+=i)
phi[j]=phi[j]/i*(i-1);
for(i=2;i<maxn;i++)
ans[i]=phi[i];
for(i=2;i<=1000;i++)
{
ans[i*i]+=phi[i]*i;
for(j=i*i+i,k=i+1;j<maxn;j+=i,k++)
ans[j]+=i*phi[k]+k*phi[i]; //i*k=j,预处理出i,k可以作为哪些数j的最大公约数
}
for(i=1;i<maxn;i++)
ans[i]+=ans[i-1];
}

int main()
{
Init();
int n;
while(scanf("%d",&n),n)
printf("%I64d\n",ans
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: