标签:memset 整数 += while i++ for 大于 理解 sqrt
问:求小于n等于且与n互素的正整数的个数
两个整数互素即:两个数gcd为1
解:
根据唯一分解定理,大于1的整数均可看成若干素数的乘积的形式
若n=1,答案为1;
若n>1;
可以这样考虑:
把n看成若干素数的乘积n=p1^k1*p2^k2*···*pm^km;(p1,p2,···,pm均为素数)(k1,k2,···,km均为非负整数)S1={p1,p2,···,pm}
那么对于满足条件的数s必有s=q1^t1*q2^t2*···*ql^tl;(q1,q2,···,qm均为素数)(t1,t2,···,tm均为非负整数)S2={q1,q2,···,qm}
s1∩S2为空集;
那么即1~n-1中所有素因子不包含S1中的数即符合题意,但是这样做太麻烦了,可以反过来考虑,
把不符合条的数去掉即可,这个可以用容斥原理解决
首先n减掉小于n且为S1中元素倍数的数量
n-n/p1-n/p2-···-n/pk;
再加上小于n且为S1中两个不同元素乘积的的数量
n-n/p1-n/p2-···-n/pm+n/(p1*p2)+n/(p1*p3)+···+n/(p(m-1)*pm);
再减去小于n且为S1中三个不同元素乘积的数量
·······
最终得到φ(n)=Σ(s?{ p1,p2,pm }(-1)^|S|*n/∏(pi∈S)pi;
但是这个公式有2^n次方项,有时不如暴力;
但是我们可以将它变形,φ(n)=n(1-1/p1)(1-1/p2)···(1-1/pm);
将上述公式展开正好符合推导过程;
根据这个我们可以编码实现
euler(int n)
{
int m=(int)sqrt(n+0.5);
int ans=n;
for(int i=2; i<=m; 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;
}
求1到n的欧拉函数值,不用一个一个计算,这样考虑,对于1~n中的任意一个素数p,其小于等于n的倍数的欧拉函数都必须乘以(p-1)/p,那么我们对于1~n所有的素数都将其倍数乘以这个值,再遍历完全部素数后,1~n的欧拉函数值都已经计算过了;
编码实现
<span>
void phi_seive(int n){
memset(phi,0,sizeof(phi));
phi[1]=1;
for(int i=2;i<=n;i++)
if(!phi[i]){
for(int j=i;j<=n;j+=i){
if(!phi[j])phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
</span>
标签:memset 整数 += while i++ for 大于 理解 sqrt
原文地址:https://www.cnblogs.com/RGBTH/p/9429298.html