版权声明:本文为湖南师范大学RBS原创文章,转载请注明出处。 http://blog.csdn.net/u012061345/article/details/24034457
欧拉函数phi(x)是指不大于正整数x的与x互质的正整数的个数。例如phi(1)=1,phi(2)=1,phi(3)=2,phi(4)=2,phi(5)=4,phi(6)=2等等。很显然,对每一个质数p,phi(p)=p-1。而对每一个质数的幂phi(p^n)=(p-1)×p^(n-1)。欧拉函数是积性函数,如果m、n互质,那么phi(m×n)=phi(m)×phi(n)。
欧拉函数的取值需要用到算术基本定理。将n写成其质因子幂的连乘积的形式,n=p1^k1×p2^k2×…×pr^kr。那么,phi(n)=n×(1-1/p1)×…×(1-1/pr)。利用这个公式可以写出求欧拉函数的代码。当然,首先要求质数。如果问题范围是MAXSIZE,那么只需求出SIZE=sqrt(MAXSIZE)以内的质数即可。即保证任何一个数的最小质因子一定在这其中。
- int euler(int n){
- int ret = n;
- for(int i=0;P[i]*P[i]<=n;++i){ //P是保存质数的数组
- if ( n % P[i] ) continue;
- ret -= ret / P[i];//计算欧拉函数
- n /= P[i];
- while( 0 == n % P[i] ) n /= P[i];
- if ( 1 == n ) break;//这三行用于加速并有助于最后得到大的质因子
- }
- if ( n != 1 ) ret -= ret / n; //假如n含有超过SIZE的质因子
- return ret;
- }
利用欧拉函数的积性性质,可以将一定范围内的欧拉函数值全部求出。任何一个合数n都可以写作n=k×p^r。其中p是一个质数,通常就会取n的最小的质因子。此时可以保证k与p^r肯定是互质的。所以phi(n)=phi(k)×phi(p^r)=phi(k)×(p-1)×p^(r-1)。当r大于1的时候,该式可以写作phi(n)=phi(k)×(p-1)×p^(r-2)×p=phi(k)×phi(p^(r-1))×p=phi(n/p)×p。而当r等于1的时候,明显有phi(n)=phi(k)×(p-1)=phi(n/p)×(p-1)。因为是顺序求值,所以在求n的欧拉函数值时,所有比n小的欧拉函数值均已求出。当然,这种方法同样要求事先筛出质数。
- int Euler[SIZE] = {0};
- void euler(){
- Euler[1] = 1;
- for(int i=2;i<SIZE;++i){
- if ( !isComp[i] ){//isComp[i]标识i是否为合数,可以由筛法得到
- Euler[i] = i - 1;
- continue;
- }
- for(int j=0;j<PCnt;++j){
- if ( i % P[j] ) continue;
- if ( i / P[j] % P[j] ) Euler[i] = Euler[i/P[j]] * ( P[j] - 1 );
- else Euler[i] = Euler[i/P[j]] * P[j];
- break;//上述式子只需计算一次即可
- }
- }
- }
上述原则求欧拉函数只需要知道n的其中一个质因子即可,一般而言就用最小质因子。所以配合线性时间素数筛法,可以直接在线性时间内求出范围内的所有质数以及每个数的最小质因子,计算欧拉函数值也是线性的。
自己的补充 ,觉得方法二的可读性有点低,一些地方没说清楚,我自己按照思路写了如下方法二的代码
int Euler_Fomuler2(int n)
{
int i,ret;
ret = 1;
for(i = 0;P[i] * P[i] < n;i++)
{
if(n % P[i]) continue;
while(n % P[i] == 0)
{
ret *= P[i];
n /= P[i];
}
ret -= ret/P[i];
}
if(n != 1)
{
ret *= (n-1);
}
return ret;
}
以下是生成P的代码
void make_p()
{
int t[300];
memset(t,0,sizeof(t));
int i,j;
t[0] = 1;
t[1] = 1;
for(i = 2;i < sqrt(double(300));i++)
{
j = i*2;
while(j < 300)
{
t[j] = 1;
j += i;
}
}
for(i = 2;i < 300;i++)
{
if(t[i] == 0)
{
P[pos++] = i;
}
}
}