1. 程式人生 > 實用技巧 >數論知識小結 [微提高篇]

數論知識小結 [微提高篇]

數論知識小結 [微提高篇]

(lastest updated on 2020.08.12)

二次剩餘和高次剩餘

\(y^c\equiv x\pmod P\)\(y\)\(x\)\(P\)\(c\)次剩餘

關於二次剩餘

\[\ \]

\(\text{Miller_Rabin}\)素數檢測

\(x\)是質數的必要條件是

\(\forall a,a^{x-1}\equiv 1\pmod x\)

同於對於一個質數\(x\),必然有

\(a^2\equiv 1\pmod x\)的解只有\(1,x-1\)

證明是

\(\because a^2\equiv 1 \pmod x\)

\(\therefore (a-1)(a+1)\equiv 0 \pmod x\)

因為\(x\)是質數,所以\(a-1\mod x=0\)\(a+1\mod x=0\),即\(a\in\{1,x-1\}\)

\[\ \]

\(\text{Miller_Rabin}\)演算法的步驟

\(x-1\)分解為\(x-1=2^s\cdot t\)

找一個\(<x\)的質數\(a\),求出\(b\equiv a^t \pmod x\)

\(b\)進行\(s\)次平方,設這一次平方的結果\(b^2\equiv c \pmod x\)

當出現\(c=1\)時,\(b\)只能為\(1\),\(x-1\)否則\(x\)就不是質數

\(s\)次平方後,\(b\equiv a^{x-1}\pmod x\)

,若\(b\ne 1\),則\(x\)不是質數

不知道為什麼,模板題跑5次就能過了。。。

注意\(x\leq 2\or 2|x\)要特判

注意取模需要快速乘

int Miller_Rabin(ll x){
	if(x==2) return 1;
	if(x<=1 || ~x&1) return 0;
	ll s=0,t=x-1;
	while(~t&1) s++,t>>=1;
	rep(i,1,20) {
		ll a=prime[rand()%primecnt+1],b=qpow(a,t,x),c;
		rep(j,1,s) {
			c=qmul(b,b,x);
			if(c==1 && b!=1 && b!=x-1) return 0;
			b=c;
		}
		if(b!=1) return 0;
	}
	return 1;
}


\[\ \]

\(\text{Pollard's_Rho}\)質因數分解

核心就是名字裡的Rho(\(\rho\)),是偽迴圈的一個形象的表示

偽迴圈:從某一個時刻開始,進入一個真迴圈,之前的時間就是\(\rho\)的腳

構造偽隨機函式\(G_n(x)=(x^2+c)\mod n\)

構造數列\(a_i=G_n(a_{i-1})\)

由於函式的值域只有\([0,n-1]\),必然出現偽迴圈,即在從個位置開始,進入一個未知長度的迴圈,也就是長成了一個\(\rho\)的形狀

由於這個函式是偽隨機函式,所以這個迴圈大小在期望情況下是\(O(\sqrt n)\)

\[\ \]

\(\text{Pollard's_Rho}\)演算法要找到一個\(p\in[2,n-2],p|n\)

考慮用\(\text{Floyd}\)演算法找環,即定義兩個變數,一個每次走一步,一個每次走兩步,設他們為\(x,y\)

\(x=y\)時,顯然出現迴圈

由於\(p|n\),所以當\(x \equiv y \pmod p\)時,實際上是\(G_p(x)\)這個函數出現了迴圈

所以在找\(G_n(x)\)的迴圈時,可以通過求出\(\gcd(x-y,n)\)判斷是否出現\(G_p(x)\)的迴圈

注意如果出現\(x=y\)情況已經找到\(n\)的迴圈,說明這個我們這次構造的這個函式找不到\(p\)的迴圈

由於\(\forall n\notin prime,\exist p\in[1,\sqrt n],p|n\)

所以期望情況下每\(\sqrt p\leq \sqrt {\sqrt n}=n^{\frac{1}{4}}\)的長度會出現迴圈

演算法複雜度是期望\(O(n^{\frac{1}{4}}\log n)\)

那麼寫出\(\text{Pollard's_Rho}\)演算法的程式碼

ll Pollards_Rho(ll n){
	ll c=rand(); // 隨機生成一個函式
    ll x=rand(),y=x,d=1; // 隨機一個初始值
    while(d==1){
		x=(qmul(x,x)+c)%n;
		y=(qmul(y,y)+c)%n;
		y=(qmul(y,y)+c)%n;
		d=gcd(n,abs(x-y));
	}
    if(d==n) return Pollards_Rho(n); // 構造失敗
    else return d; // 找到了p
}

不斷呼叫即可完成對於n的質因數分解

對於質因數分解,更高階的演算法可以參考LOJ-6466

莫比烏斯函式

\(n=\prod_1^m p_i^{c_i}\),其中\(c_i>0,p_i\)為質數

則莫比烏斯函式 \(\mu(n)=\left\{\begin{aligned}1 && n=1\\ (-1)^m && \nexists c_i>1 \\ 0 && \exists c_i>1\end{aligned}\right.\)

狄利克雷卷積

對於數列\(F,G\),他們的狄利克雷卷積(下簡稱\(F\oplus G\))為

\[\begin{aligned} (F\oplus G)_i=\sum_{d|i}F_d\cdot G_{\frac{i}{d}}\end{aligned} \]

\[\ \]

莫比烏斯反演

設元函式\(E_i=1\)

\(G=F\oplus E\),即\(G_i=\sum_{d|i}F_d\)

\(G\)反解\(F\)得到莫比烏斯反演\(F_i=\sum_{d|i}\mu(d) G_{\frac{i}{d}}\)

\[\ \]

積性函式

積性函式的定義,對於一個定義在\(\Z\)上的函式\(F(n)\),若滿足

\(F(1)=1,\forall (u,v)=1,F(u)\cdot F(v)=F(u\cdot v)\),則\(F(u)\)是一個積性函式

完全積性函式對於任意的\(u,v\)對滿足上述性質

常見的積性函式有

1.元函式\(e(n)=[n=1]\)

2.因數個數函式\(d(n)\)

3.尤拉函式\(\varphi(n)\)

4.莫比烏斯係數\(\mu(n)\)

5.約數和函式\(\sigma(n)\)

推論:任意兩個積性函式的狄利克雷函式卷積 仍然是積性函式

線性篩篩法求解積性函式

把積性函式\(F(n)\)表示為

\(F(n)=\left\{\begin{aligned} 1 && n=1 \\ G(n) && n=p_i^t \\ \prod G(p_i^{c_i}) && n=\prod p_i^{c_i}\end{aligned}\right.\)

如果能在較短的時間內求得\(G(p_i^t)\),則可以用線性篩法求解積性函式\(F(n)\)的前\(n\)

一個最簡單的應用: 在\(O(n)\)時間求解\(id^z(n)=n^z\)

顯然,\(id^z(n)\)是一個完全積性函式,且直接求複雜度為\(O(n\log z)\)

因為是完全積性函式,所以只需要求解\(id^z(p_i)\),這一部分複雜度為\(O(\pi(n)\cdot \log z)=O(n)\)

線性篩法的複雜度為\(O(n)\),因此總複雜度也為\(O(n)\)

(這就是傳說中的魔法嗎!!)

一個簡單的應用:求解\(\mu(n)\)

鑑於\(\mu(n)\)的特殊性,也只需要求出\(\mu(p_i)\)

寫出的程式碼大致是這樣的

int pri[N],notpri[N],pc,mu[N];
void Sieve_Mobius(int n){
    mu[1]=1;
    for(int i=2;i<=n;++i) {
        if(!notpri[i]) pri[++pc]=i,mu[i]=1;
        for(int j=1;j<=pc && 1ll*i*pri[j]<=n;++j) {
            notpri[i*pri[j]]=1;
            if(i%pri[j]==0) {
                mu[i*pri[j]]=0;
                break;
            }
            mu[i*pri[j]]=-mu[i];
        }
    }
}
真-應用: 大型模板
int CalcG(int n);

int prime[N],primecnt,notprime[N];
int F[N],D[N];
// F儲存函式值
// D儲存質因數出現的冪次積

void Sieve_Multiplicative_Function(int n){
    F[1]=1;
    for(int i=2;i<=n;++i){
        if(!notprime[i]) {
            prime[++primecnt]=i;
            for(ll j=i;j<=n;j*=i) F[j]=CalcG(j),D[j]=j;
            // 計算F(p_i^t)
        }
        for(int j=1;j<=primecnt && 1ll*i*prime[j]<=n;++j) {
            notprime[i*prime[j]]=1;
            int k=i*prime[j];
            if(i%prime[j]==0) {
                D[k]=D[i] * prime[j];
                F[k]=F[i/D[i]] * F[D[k]];
                break;
            }
            D[k]=prime[j];
            F[k]=F[i] * F[prime[j]];
        }
    }
}

\[\ \]

\[\ \]

杜教篩

用於求解 較大範圍可以構造出一些性質的積性函式 字首和

不推薦看我的,但是還是放一下連結

\[\ \]

Min25篩

用於求 較大範圍使用範圍更廣 的積性函式字首和 , 但在效率上不敵杜教篩

還是放一下連結