欧拉函数求法与欧拉筛法求素数
欧拉函数:
欧拉函数定义:
对于正整数n,欧拉函数Euler(n)是1到n-1中与n互质的数的个数,特别的,Euler(1) = 1,若n为质数则有 Euler(n) = n - 1
欧拉函数的两种求法:
1.由定义和常识可以知道对于任意一个素数n有 Euler(n) = n - 1,对于m = n ^ k,Euler(m)是非常好求解的,显然,只有n的倍数才是不满足欧拉函数的定义的数,只要减去即可。得:Euler(m) = n ^ k - n ^ (k - 1)。
另外,附加介绍以一点关于简化剩余系的概念,取定m > 0,若r mod m 中的每个数都与m互素,则称r mod m是与m互素的剩余类。从所有与模m互素的剩余类中各取一数所组成的一组数称为简化剩余系。如 m = 5 有 1 mod 5, 2 mod 5, 3 mod 5, 4 mod 5,是与m互素的剩余类。易知模m的一个简化剩余系中一共有Euler(m)个数,有定理:设(m1, m2) = 1,若x1,x2分别通过模m1,m2的一个简化剩余系,则m1x2 + m2x1通过m1m2的一个简化剩余系。通过这个定理我们可以得到Euler(m * n) = Euler(m) * Euler(n)。
对于任意一个数,可以进行整数分解,分解成为 m = n1 ^ x1 * n2 ^ x2 * ni ^ xi,这样根据上面的两个结论我们可以得到Euler(m) = Euler(n1 ^ x1) * ... * Euler(ni ^ xi) = ((n1 ^ x1) - (n1 ^ (x1 - 1))) * ... * ((ni ^ xi) - ni ^ (xi - 1)) = (n1 - 1) * (n1 ^ (x1 - 1)) * ... * (ni - 1) * (ni ^ (xi - 1)),根据这个式子我们可以推出如下的性质:设n为m的质因数则有(m % n == 0) (1)若 (m / n) % n != 0 有E(m) = E(m / n) * (n - 1),这里就是前面式子中每个质因数的前面的(ni - 1)。(2)若有(m / n) % n == 0的话,则是E(m) = E(m / n) * n,对应的时候前面式子中每一质因数的xi次幂。
对于上述的式子,可以得到第一种方法来求解欧拉函数。代码如下:
int euler(int n){ int ans = 1; for(int i = 2; i * i <= n; i ++){ if(n % i == 0){ n /= i; ans *= i - 1; while(n % i == 0){ n /= i; ans *= i; } } } if(n > 1) ans *= n - 1; return ans; }
当然这里给出欧拉函数的第二种揭解法,我们知道,任何一个数都可以分解成为几个素数乘积的形式,那么比如1001 = 7 * 11 * 13,根据我们之前推出的,其实只要减去7,11,13这些质数的倍数,剩下的就都是与其互质的数,那么这里涉及到一个新的名词——容斥定理,容斥定理在这里的应用是去掉7的倍数,11的倍数,13的倍数,然后加上7和13的倍数,11和13的倍数,7和13的倍数,再减去7和11和13的倍数。
根据容斥定理,有以下代码求解欧拉函数:
int euler(int n){ int 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; }
欧拉筛法求素数:
欧拉筛法求素数又称为线性筛法求素数,是一种将求一定范围内的素数的时间复杂度控制在O(n)的一种算法,它跟埃拉特斯尼筛法求素数很像,做了一定的改进,我们知道,埃拉特斯尼筛法之所以没有达到线性筛的水平是因为它对于同一个合数,要重复操作他的质因子个数次,这是很浪费的,而欧拉筛法求素数则去掉了重复操作,它在当合数i可以整除质数时跳出对下一个数进行操作,比方说,当i = 4时,不会筛掉12这个数因为12找到他的最小的质因数是2,12 = 2 * 6,6定义为它相对最大的合数因子,那么4不满足这个条件,所以不在i =4 时筛掉12,而是在i = 6的时候筛掉12.这样就避免了重复操作。
代码如下:
bool IsPrime[1000010]; int Prim[1000010]; int euler_prime(int n){ int num = 0, j; for(int i = 2; i <= n; i ++){ if(!IsPrime[i]) Prim[num ++] = i; for(j = 0; j < num; j ++){ if(i * Prim[j] > n) break; IsPrime[i * Prim[j]] = true; if(i % Prim[j] == 0) break; } } //for(int i = 0; i < num; i ++){ // cout << Prim[i] << endl; //} }