欧拉函数的一些模板+注释

来源:互联网 发布:淘宝网店如何起步 编辑:程序博客网 时间:2024/05/18 02:16

将要学习数论,选择一套题来拓展知识面。机缘巧合之下就理解了埃氏素数筛法,和线性筛法的相似之处,特记下模板以及理解。
简单说一下欧拉函数(来源百度百科):在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1)。比如:φ(10)=4,那么与他互质的数有哪些呢? 1,3,7,9。
而求欧拉函数值有一个通式:φ(x)=x*(1-1/x1)(1-1/x2)…
而这些x1,x2等等都是x的质因数,什么是质因数呢?首先要先是质数(素数),其次也要是x的因子,比如x=10,那么x1=2,x2=5,代入得φ(10)=10*(1/2)*(4/5)=4。
但是呢,类似于素数,我们用的时候往往是比较很大很大的范围的数,所以既然素数都有素数筛法打表,那么就会有筛法欧拉函数。
类似于素数筛法,都是找因子的倍数关系的数,素数筛法原理就是2既然是素数,那么2的倍数就不再是素数,统统打死。。。而筛法欧拉函数的原理就根据那个通式来完成的,通式里面,要求x1,x2是质因子,而素数筛法则可以很好的吧所有素数过滤出来,那么每过滤一个素数,那么以它为质因子的数是不是都可以进行一下那个通式的运算?对,这就是原理。
附上代码:

//筛法欧拉函数<by kuangbin>int euler[1000000+10];void geteuler(){    mem(euler,0);    euler[1]=1;    for(int i=2;i<=1000000;i++)    {        if(!euler[i])        {            for(int j=i;j<=1000000;j+=i)            {                if(!euler[j])                {                    euler[j]=j;                }                euler[j]=euler[j]/i*(i-1);            }        }    }}

接着,若只是简单的计算一下单个或少量值得欧拉函数值,就不需要打表啦,所以这个时候,计算单个数的欧拉函数值就应运而出了。

LL euler(LL n){    LL ans=n;    for(int i=2;i*i<=n;i++)    {        if(n%i==0)        {            ans-=ans/i;            while(n%i==0)            {                n/=i;            }        }    }    if(n>1) ans-=ans/n;    return ans;}

下面解释一下代码。。。看了几十分钟,也是没谁了。。。
首先,我们想一下,怎么去求一个数n的欧拉函数值,答案是求出n的所有质因子,然后套公式。但关键之处有两点,怎么筛选出素数,怎么利用公式求欧拉函数值。
这几行代码,虽然简单,但是却又好多东西。(一点一点解释)
最外层的 for循环和if(n%2)用于判断素数,(可以回忆下判断一个数是不是素数的模板的那一层for循环(for(int i=2;i<=sqrt(n);i++)),但是要记得,这里的i表示的质因子并不能代表全部,因为i(max)=sqrt(n),举个例子,6的话,i表示的质因子只能到2,3的话i循环不到。所以才有了n/=i的特判。
感觉读的人会迷迷糊糊,所以,继续解释, ans-=ans/i;这一行代码变化一下形式就是ans=ans(i-1)/i;似曾相识对吧,上文中有提到过,这就是通式的一部分。我看的时候,在想,既然都已经把所有质因子都代入通式了,为什么还要n去变化,去变小。因为有的一部分质因子在for循环里并没有加入到通式里,如果没加入,n就不会是1,if(n>1) ans-=ans/n;因为任何一个数都可以拆成质因子的乘积,但是,我就想了,难道不在for循环里的质因子只有一个吗?为什么只要一次加入通式就可以了?因为如果有多个质因子没有在for循环里加入通式的话,比如(当然这只是错误的例子)10,假如2,5没有加入通式,那么n的值是10(2*5),那么 ans-=ans/n这个式子分别代入2,5和直接代入10是不一样的,所以我就猜测是不是只剩一个质因子不在for循环里。
那么接着验证我的猜测,重点还是在一句话,所有大于1的正整数都可以拆成质因子的乘积,举个例子,10,sqrt(10)近似为3,所以,剩5不在for循环,若是有质因子不在for循环里,只可能是最大的一个。
还有一点疑惑哦,是为什么n每次除以i,依旧举个例子,n=8,当循环过i==2时,n变成了4,那么若是n能被除成1,说明后面不在有质因子。你觉得呢。
然后呢,就是最后一种代码模板了。。。
线性筛法(同时得到欧拉函数值和素数表)

bool check[1000000+10];int phi[1000000+10];int prime[1000000+10];int tot;//素数的个数void phi_and_prime(){    mem(check,0);    phi[1]=1;    tot=0;    for(int i=2; i<=1000000; i++)    {        if(!check[i])        {            prime[tot++]=i;            phi[i]=i-1;//若i是素数,那么φ(i)=i-1。(定理)        }        for(int j=0; j<tot; j++)        {            if(i*prime[j]>1000000) break;//只求1000000以内的素数            check[i*prime[j]]=1;//素数的倍数便不是素数            if(i%prime[j]==0)//用于求欧拉函数值            {                phi[i*prime[j]]=phi[i]*prime[j];                break;            }            else            {                phi[i*prime[j]]=phi[i]*(prime[j]-1);            }        }    }}

留下一个自己钻研去吧。。。。0.0

1 0