1. 程式人生 > 實用技巧 >尤拉函式學習筆記

尤拉函式學習筆記

原理

請思考以下問題:

任意給定正整數n,請問在小於等於n的正整數之中,有多少個與n構成互質關係?(比如,在1到8之中,有多少個數與8構成互質關係?)

計算這個值的方法就叫做尤拉函式,以φ(n)表示。在1到8之中,與8形成互質關係的是1、3、5、7,所以 φ(n) = 4。

φ(n) 的計算方法並不複雜,但是為了得到最後那個公式,需要一步步討論。

第一種情況

如果n=1,則 φ(1) = 1 。因為1與任何數(包括自身)都構成互質關係。

第二種情況

如果n是質數,則 φ(n)=n-1 。因為質數與小於它的每一個數,都構成互質關係。比如5與1、2、3、4都構成互質關係。

第三種情況

如果n是質數的某一個次方,即 n = p^k (p為質數,k為大於等於1的整數),則

\(\phi\left(p^{k}\right)=p^{k-p^{k-1}}\)

比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。

這是因為只有當一個數不包含質數p,才可能與n互質。而包含質數p的數一共有p^(k-1)個,即1×p、2×p、3×p、...、p^(k-1)×p,把它們去除,剩下的就是與n互質的數。

上面的式子還可以寫成下面的形式:

\(\phi\left(p^{k}\right)=p^{k-p k-1}=p^{k}\left(1-\frac{1}{p}\right)\)

可以看出,上面的第二種情況是 k=1 時的特例。

第四種情況

如果n可以分解成兩個互質的整數之積,

n = p1 × p2

φ(n) = φ(p1p2) = φ(p1)φ(p2)

即積的尤拉函式等於各個因子的尤拉函式之積。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24。

這一條的證明要用到"中國剩餘定理",這裡就不展開了,只簡單說一下思路:如果a與p1互質(a<p1),b與p2互質(b<p2),c與p1p2互質(c<p1p2),則c與數對 (a,b) 是一一對應關係。由於a的值有φ(p1)種可能,b的值有φ(p2)種可能,則數對 (a,b) 有φ(p1)φ(p2)種可能,而c的值有φ(p1p2)種可能,所以φ(p1p2)就等於φ(p1)φ(p2)。

第五種情況

因為任意一個大於1的正整數,都可以寫成一系列質數的積。
\(n=p_{1}^{k_{1}} p_{2}^{k_{2} \ldots p_{r} r}\)

根據第4條的結論,得到
\(\phi(n)=\phi\left(p_{1}^{\left.k_{1}\right)} \phi\left(p_{2}^{k_{2}}\right) \ldots \phi\left(p_{r r}^{k_{r}}\right)\right.\)

再根據第3條的結論,得到

\(\phi(n)=p_{1}^{k_{1}} p_{2}^{k_{2}} \ldots p_{r}^{k_{r}} \left(1-\frac{1}{p_{1}}\right)\left(1-\frac{1}{p_{2}}\right) \ldots\left(1-\frac{1}{p_{r}}\right)\)

也就等於

\(\phi(n)=n\left(1-\frac{1}{p_{1}}\right)\left(1-\frac{1}{p_{2}}\right) \ldots\left(1-\frac{1}{p_{r}}\right)\)

這就是尤拉函式的通用計算公式。比如,1323的尤拉函式,計算過程如下:

\(\phi(1323)=\phi\left(3^{3} \times 7^{2}\right)=1323\left(1-\frac{1}{3}\right)\left(1-\frac{1}{7}\right)=756\)

性質

(1)對於質數\(n,φ(n)=n−1\)

(2)對於\(n=p^k,φ(n)=(p−1)∗p^{k−1}\)

(3)對於 \(n=\prod p_{i}^{k_{i}}\)\(\varphi(n)=n * \prod\left(1-\frac{1}{p_{i}}\right)\)

(4) 對於\(gcd(n,m)=1\)\(\varphi(n * m)=\varphi(n) * \varphi(m)\)

(5) 【尤拉定理】 對於互質的\(a,m\)\(a^{\varphi(m)} \equiv 1 \quad(\bmod m)\)

(6) 小於n且與n互質的數的和:\(S=n * \frac{\varphi(n)}{2}\)

(7) 對於質數p

\(n \quad \bmod p=0\), $ φ(n∗p)=φ(n)∗p$,

\(n \quad \bmod p \neq 0\)\(\varphi(n * p)=\varphi(n) *(p-1)\)

(8)\(\sum_{d \mid n} \varphi(d)=n\)

\(\varphi(n)=\sum_{d \mid n} \mu(d) * \frac{n}{d}\)

模板

直接質因數分解法

//POJ 2407,給定n,求小於n且和n互質的數個數
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
const ll maxp=1e5+5;
bool isPrime[maxn];
ll Prime[maxp], primecnt = 0;
void GetPrime(ll n){//篩到n
	memset(isPrime, 1, sizeof(isPrime));
	isPrime[1] = 0;//1不是素數
	for(ll i = 2; i <= n; i++){
		if(isPrime[i])//沒篩掉
			Prime[++primecnt] = i; //i成為下一個素數
		for(ll j = 1; j <= primecnt && i*Prime[j] <= n; j++) {
			isPrime[i*Prime[j]] = 0;
			if(i % Prime[j] == 0)//i中也含有Prime[j]這個因子
				break;
		}
	}
}
ll getol(ll n){
    if(n==0){
        return 0;
    }
    ll ans=n;
    ll temp=n;
    for(ll i=1;i<=primecnt;i++){
        if(temp%Prime[i]==0){
            ans=ans*(Prime[i]-1)/Prime[i];
            while(temp%Prime[i]==0)
                temp/=Prime[i];
        }
        if(temp<Prime[i]*Prime[i]){
            break;
        }
    }
    if(temp>1){
        ans=ans*(temp-1)/temp;
    }
    return ans;
}
int main () {
    GetPrime(maxn-5);
    ll n;
    while(~scanf("%lld",&n)){
        if(n==0){
            break;
        }
        ll ans=getol(n);
        printf("%lld\n",ans);
    }
}

線性素數篩法

原理:

  • 若i%p=0,p是素數,那麼\(φ(i*p)=φ(i)*p\)
  • 若i%p!=0,p是素數,那麼\(φ(i*p)=φ(i)*(p-1)\)

證明:

2式可以直接由積性函式的性質(性質4)推出,1式證明如下:

\(i\)轉換為\(p^n*k\)的形式,則\(φ(i)=p^{n}*\frac{p-1}{p}*φ(k)\)\(φ(i*p)=φ(p^{n+1}*k)\),由於\(p^{n+1}\)\(k\)互質,因此\(φ(i*p)=φ(p^{n+1}*k)=φ(p^{n+1})*φ(k)=p^{n+1}*\frac{p-1}{p}*φ(k)=p*φ(i)\)

程式碼:

//HDU1286,T組資料,詢問n的尤拉函式,n<32768
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int maxp=1e6+5;
bool isprime[maxn];
int prime[maxp], primecnt = 0;
int ol[maxn];
void getOL(int n){//篩到n
	memset(isprime, 1, sizeof(isprime));
	isprime[1] = 0;//1不是素數
	ol[1]=1;
	for(int i = 2; i <= n; i++){
		if(isprime[i]){//沒篩掉
			prime[++primecnt] = i; //i成為下一個素數
			ol[i]=i-1;
		}
		for(int j = 1; j <= primecnt && i*prime[j] <= n; j++) {
			isprime[i*prime[j]] = 0;
			if(i % prime[j] == 0){//i中也含有prime[j]這個因子
			    ol[i*prime[j]]=ol[i]*prime[j];
				break;
			}
			else{
                ol[i*prime[j]]=ol[i]*(prime[j]-1);
			}
		}
	}
}
int main(){
    getOL(32768);
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        printf("%d\n",ol[n]);
    }
}

引用與參考:

https://blog.csdn.net/paxhujing/article/details/51353672

https://www.cnblogs.com/Mychael/p/8759124.html