代码改变世界

欧拉函数

2012-05-06 00:54  youxin  阅读(1263)  评论(0编辑  收藏  举报

  数论中对正整数n,欧拉函数是小于n且与n互质的数的数目。此函数以其首名研究者欧拉命名,它又称为Euler's totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。 从欧拉函数引伸出来在环论方面的事实和拉格朗日定理构成了欧拉定理的证明。

1、递推求解

    欧拉函数可以很方便的计算小于某个数N但N互质的数的个数, 即M(1<=M<N)且gcd(M, N)=1, M的个数很容易由欧拉函数来计算出来. 欧拉函数的表达式为N*(1-1/f_1)*(1-1/f_2)*(1-1/f_3)....依次类推, 其中f_1, f_2, f_3等是N的不相同的质因子(或者这么说:其中p1, p2……pn为x的所有质因数,x是不为0的整数。φ(1)=1(唯一和1互质的数就是1本身)。 (注意:每种质因数只一个)). 例如12=2*2*3那么12有两个不同的质因子2, 3, 由欧拉函数可得小于12但与12互质的个数为12*(1-1/2)(1-1/3)=4, 列举为1, 5, 7, 11. 那么在实际实现欧拉函数的时候, 可以把一个数进行质因子分解, 依次代入欧拉函数进行求解. 我们今天介绍一种用欧拉函数自身的递推关系来实现的方法.

  首先介绍这种递推关系, 假设数N有m个不相同的质因子f_1, f_2,f_3....f_m. 那么数(N/f_1)有多少个不同的质因子呢?

分成两种情况来考虑,

   第一钟情况: N只包含一个f_1因子, 那么N/f_1有m-1个因子f_2,f_3,...,f_m. 我们考察N/f_1和N的欧拉函数形式E(N) = N*(1-1/f_1)*(1-1/f_2)*(1-1/f_3)*...*(1-1/f_m)    E(N/f_1) = N/f_1*(1-1/f_2)*(1-1/f_3)*...*(1-1/f_m). 把(1-1/f_1)化为(f_1 - 1)/f_1则可以显然看到

  E(N) = (f_1 - 1)*E(N/f_1).

  第二种情况, N包含一个以上的f_1因子, 那么N/f_1包含了与N相同的质因子个数且此时两者的欧拉函数分别记为E(N) = N*(1-1/f_1)*(1-1/f_2)*(1-1/f_3)*...*(1-1/f_m)    E(N/f_1) = N/f_1*(1-1/f_1)*(1-1/f_2)*(1-1/f_3)*...*(1-1/f_m). 这个递推关系更明显了

  E(N) = (f_1)*E(N/f_1).

因此这两种递推关系只与质因子f_1有关, 而f_1可以是N的任意一个质因子. 用代码来实现时可以取N的最小质因子来简化实现过程.

在实际代码过程可以和搜索质数的"筛子法"相结合, 因为"筛子法"相当于优先找到了每个数的最小质因子.

 

在程序中利用欧拉函数如下性质,可以快速求出欧拉函数的值(a为N的质因素)

若(N%a==0 && (N/a)%a!=0) 则有:E(N)=E(N/a)*(a-1); (只有一个a)
若(N%a==0 && (N/a)%a==0) 则有:E(N)=E(N/a)*a;

 

下面的程序是求1到10000之间所有整数的欧拉函数:

#include<iostream>
using namespace std;

char mark[10000]={0};
int prime[1230];
int size=0;
int phi[10000];

int main()
{
    int i,j;
    //prime
    for(i=2;i<10000;i++)
    {
        if(!mark[i]) prime[size++]=i;
        for(j=0;j<size&&prime[j]*i<10000;j++)
        {
            mark[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }

    //求欧拉函数

    phi[1]=1;
    for(i=2;i<10000;i++)
    {
        if(!mark[i])
        {
            phi[i]=i-1; //本身就是素数
            continue;
        }
        for(j=0;j<size&&prime[j]*prime[j]<=i;j++)
        {
            if(i%prime[j]==0)
            {
                if(i/prime[j]%prime[j]==0)
                    phi[i]=prime[j]*phi[i/prime[j]];
                else
                    phi[i]=(prime[j]-1)*phi[i/prime[j]];
                break;
            }
        }
    }
    //输出结果
    for(int i=1;i<10000;i++)
        printf("%d__%d    ",i,phi[i]);

    return 0;
}

看一下为什么prime[]数组的尺寸为1230,这就要看n内素数的个数大约多少了。

π(x)表示不大于x的素数的个数,π(x)约等于x/lnx

“当x趋向于无穷时,π(x)与x/lnx比值的极限是1”这就是传说中的素数定理。

勒让德认为更精确的式子是x/(lnx-B),其中的B=1.08366。

后来切比雪夫证明了下面两个结论: 
a、存在两个很接近1的常数C1和C2,使不等式C1*(x/lnx)<π(x)<C2*(x/lnx)成立。 
b、如果非得用式子x/(lnx-B)来估计π(x)的话,那么B=1。

高斯,欧拉等牛都搞出来了更精确的式子.不过都是概率逼近的极限再精确也差不多...

网上有很多文章对素数定理这个问题说得很详细.我就不放这了..

ps:得出结论..貌似某些题目把素数筛出来再搞好象也快不了多少哦.

知道这个,算出10000 素数约有 1086个,实际1229个.大于这个就可以了。

还可以优化:

从别人那里学到的对求欧拉函数部分的优化,使每个数的欧拉函数只由它的最小素因子求出:
phi[1] = 1;
for (i = 1; i < 10000; i++)
{
    for (j = 0; j < size && prime[j] * i <= 10000; j++)
    {
        if (i % prime[j] == 0)
        {
            phi[prime[j] * i] = prime[j] * phi[i];
            break;
        }
        else 
        {
            phi[prime[j] * i] = phi[i] * (prime[j] - 1);
        }
    }
}