「NOTE」數論小札

由於有春節,因此實際上寒假比暑假長
主要內容:原根 / 離散對數 / 二次剩餘ide


# 目錄


# 原根

下面只討論奇素數 \(p\) 的原根,在不特別強調時,運算在模 \(p\) 意義下進行。函數

- 前置:階

對於與 \(p\) 互質的常數 \(a\),在知足 \(a^x\equiv1\) 的全部正整數 \(x\) 中,最小的 \(x\) 稱爲 \(a\)\(p\) 的階,記做 \(ord(a)\)ui

Theorem 1this

當且僅當 $ord(a)\mid x$,有 $a^x\equiv 1$。spa

證實

假設 $a^x\equiv 1$ 且 $a^y\equiv 1$,則有 $a^{x+y}\equiv 1$ 且 $a^{x-y}\equiv 1$。code

展轉相除法可得 $a^{(x,y)}\equiv1$。ip

$(x,y)\le \min\{x,y\}$,又有 $ord(a)$ 是知足 $a^x\equiv1$ 的最小 $x$,因此 $ord(d)\mid x$。ci

Theorem 2rem

若 $k\mid ord(a)$,則 $ord(a^k)=\frac{ord(a)}k$get

證實

顯然有 $frac{ord(a)}k$ 是 $(a^k)^x\equiv 1$ 的解(代入方程、結合 $ord(a)$ 定義便可證實),只需證實 $\frac{ord(a)}k$ 是最小解。

假如存在 $x_0< \frac{ord(a)}k$ 也是方程的解,則 $kx_0$ 是 $a^x\equiv1$ 的解;而 $kx_0< ord(a)$,與 $ord(a)$ 定義矛盾。

根據 Theorem 1 有推論 \(ord(a)\mid (p-1)\),證實即費馬小定理。

- 原根

若在模 \(p\) 意義下,\(x^0,x^1,\cdots,x^{\varphi(p)-1}\) 互不相同,也即 \(ord(x)=\varphi(p)\),則稱 \(x\)\(p\) 的原根,記爲 \(\xi\)

實際上只有形如 \(p^k,2p^k\) 的數以及 \(2,4\) 有原根(雖然我不會證)。

而根據原根的定義,模 \(p\) 意義下的任意元素均可以表示爲 \(\xi^k\)原根的一個重要做用)。

Theorem 3

設 $\xi$ 是質數 $p$ 的一個原根,則 $\xi^k$ 是原根當且僅當 $(k,p-1)=1$。

因爲任意元素均可以用 \(\xi^k\) 表示。

假設 \(\xi^k\)\(p\) 的原根,則必須 \(ord(\xi^k)=\varphi(p)=p-1\)。令 \(h=(k,p-1)\)

\[(\xi^k)^{\frac{p-1}h}\equiv(\xi^{p-1})^{\frac kh}\equiv 1 \]

則有 \(ord(\xi^k)\mid\frac{p-1}h\)

  • \(h\neq 1\),則顯然 \(\xi^k\) 不是 \(p\) 的原根;
  • \(h=1\),由於 \((\xi^k)^{ord(\xi^k)}\equiv1\),則有 \(\xi^{k\cdot ord(\xi^k)}\equiv1\)\(p-1\mid ord(\xi^k)\),所以 \(ord(\xi^k)=p-1\),也即 \(\xi^k\)\(p\) 的原根。

根據 Theorem 3,能夠知道 \(p\) 的原根恰有 \(\varphi(p-1)\) 個。

補充一個講課的dalao不會證因此我也不會證的定理:

Theorem 4(二次互反律)

$p,q$ 均爲奇素數,則

$$\left(\frac pq\right)\cdot\left(\frac qp\right)=(-1)^{\frac{p-1}2\times \frac{q-1}2}$$

- 計算原根

直接從小到大枚舉 awa

注意到 \(p\) 的原根有 \(\varphi(p-1)\) 個,其實仍是蠻多的,因而枚舉不了多少個就能夠找到一個最小的原根 \(\xi_0\)

而後再枚舉 \((k,p-1)=1\)\(k\),就能夠找出剩下的原根 \(\xi_0^k\) 了。(通常不會真的讓你求全部原根)

怎麼驗算數 \(x\) 是否是 \(p\) 的原根?

  • 先驗算是否 \(ord(x)\mid \varphi(p)\),直接快速冪檢驗 \(x^{\varphi(p)}\) 是否爲 \(1\) 便可;
  • 再驗算 \(ord(x)\) 是否剛好是 \(\varphi(p)\):把 \(p-1\) 的全部質因子找出來記爲 \(P=\{p_1,p_2,\dots,p_m\}\),若 \(\forall p_i\in P,x^{\frac{p-1}{p_i}}\not\equiv1\),則 \(ord(x)=\varphi(p)\)\(x\) 是原根。

- 參考實現1

int nprm,nfc,nans;
int prm[N],phi[N],vis[N],fc[N],ans[N];
bool ifrt[N]; //ifrt[i]=true: i有原根

void Init(){
    //線性篩篩素數和歐拉函數
    phi[1]=1;
    for(int i=2;i<N;i++){
        if(!vis[i]) prm[++nprm]=i,phi[i]=i-1,vis[i]=i;
        for(int j=1;j<=nprm && prm[j]*i<N;j++){
            vis[prm[j]*i]=prm[j];
            if(i%prm[j]==0) {phi[i*prm[j]]=phi[i]*prm[j];break;}
            phi[i*prm[j]]=phi[i]*(prm[j]-1);
        }
    }
    //2,4,prime^k,2*prime^k 纔有原根
    ifrt[2]=ifrt[4]=true;
    for(int i=2;i<=nprm;i++){
        for(llong j=prm[i];j<N;j*=prm[i]) ifrt[j]=true;
        for(llong j=2*prm[i];j<N;j*=prm[i]) ifrt[j]=true;
    }
}
int GCD(cint a,cint b){return b? GCD(b,a%b):a;}
int QPow(int a,int b,cint MOD){
    int r=1;
    while(b){
        if(b&1) r=1ll*r*a%MOD;
        a=1ll*a*a%MOD;
        b>>=1;
    }
    return r;
}
//把 p 的全部質因子存入 fc[1~nfc] 中
void PrimeDivide(int p){
    int las=-1; nfc=0;
    while(p>1){
        if(las!=vis[p]) fc[++nfc]=vis[p];
        las=vis[p];
        p/=vis[p];
    }
}
bool Check(cint x,cint p){
    //先驗算 x 是否知足 ord(x)|phi[p]
    if(QPow(x,phi[p],p)!=1) return false;
    //而後驗算 ord(x) 是不是 phi[p]
    for(int i=1;i<=nfc;i++)
        if(QPow(x,phi[p]/fc[i],p)==1)
            return false;
    return true;
}
int MinRoot(cint p){ //找最小的原根
    PrimeDivide(phi[p]);
    for(int i=1;i<p;i++)
        if(Check(i,p))
            return i;
    return 0;
}
void AllRoot(cint p){
    nans=0;
    int per=MinRoot(p),prod=1;
    for(int i=1;i<=phi[p];i++){ //拓展出全部的原根
        prod=(1ll*prod*per)%p;
        if(GCD(i,phi[p])==1) ans[++nans]=prod;
    }
}

# 離散對數

- 引入問題

求出下面方程的全部非負整數解:

\[a^x\equiv b\pmod p \]

其中知足 \((a,p)=1\);亦可擴展至 \((a,p)\neq 1\)

- BSGS(大步小步)

根據歐拉定理,當 \((a,p)=1\) 時,有 \(a^{\varphi(p)}\equiv1\)。記原方程的最小正整數解爲 \(x_0\),則原方程的解 \(x\) 能夠表示爲 \(x=x_0+k\varphi(p)\),因而只須要求 \(x< \varphi(p)\) 的解。

\(q=\lfloor\sqrt p\rfloor\),將原方程的解 \(x\) 分解爲 \(x=nq-m\),其中 \(0\le m< q\)。易知 \(n\) 的數量級與 \(q\) 同階。

原方程能夠寫爲:

\[\begin{aligned} a^{nq-m}&\equiv b\\ a^{nq}&\equiv ba^m \end{aligned} \]

因爲 \(n,m\) 都是 \(O(\sqrt p)\) 級別的,能夠先 \(O(\sqrt p)\) 將方程一邊的全部取值都存進哈希表,而後另外一邊 \(O(\sqrt p)\) 查詢哈希表中是否有對應的值。

例如,將 \(m=0,1,\cdots,q-1\) 時的所有 \(ba^m\) 存入哈希表,而後枚舉 \(n\),查找哈希表中是否存在 \(a^{nq}\)

- exBSGS

以前提到離散對數能夠擴展到 \((a,p)\neq 1\) 的狀況,下面進行推導。

同餘方程 \(a^x\equiv b\pmod p\) 有以下性質:

  • \((a,p)=g\)
  • \(g\not\mid b\),則無解,能夠從同餘方程的本質理解:\(a^x=kp+b\),則 \(b=a^x-kp\),如有解則 \(b\) 必然是 \(g\) 的倍數;
  • \(g\mid b\),則等價爲 \(\frac{a^x}g\equiv\frac bg\pmod{\frac{p}{g}}\)

因而能夠轉化爲

\[\frac ag\cdot a^{x-1}\equiv\frac bg\pmod{\frac pg} \]

方程左邊至關於帶了一個係數,注意到 \(a\) 仍有可能和 \(\frac pg\) 不互質,只須要一直將方程同除以 \((a,p)\),直到 \(a,p\) 互質便可。

記每一次方程總體除的數是 \(g_i\),\(G=\prod g_i\),最後方程的形式大概是

\[\frac{a^k}G\cdot a^{x-k}\equiv\frac bG\pmod{\frac pG} \]

求解方法仍是和 BSGS 同樣,用哈希表暴力儲存方程一邊的全部取值。

注意到咱們並不能保證不存在 \(x< k\) 的解。在轉化過程當中咱們直接從 \(a^x\) 中提取出一個 \(a\) 而剩下 \(a^{x-1}\) 實際上是不嚴謹的,由於轉化時 \(a,p\) 不互質,則 \(a^{-1}\) 不存在。

因而還要特判一下 \(x<k\) 有沒有解。

- 參考實現2

/*
Hs 是手寫的一個哈希表,做用至關於 map
*/
int GCD(int a,int b){return b? GCD(b,a%b):a;}
int exBSGS(int vara,int varb,int varp){
    int total=1,add=0,mul=1,tmp=0,nowp=varp;
    while((tmp=GCD(vara,nowp))!=1)
        nowp/=tmp,total*=tmp,mul=1ll*(vara/tmp)*mul%varp,add++;
    tmp=1;
    for(int i=0;i<add;i++,tmp=1ll*tmp*vara%varp)
        if(tmp==varb)
            return i;
    if(varb%total) return -1;
    varb/=total;
    //mul*vara^(x-add)=varb (mod nowp)
    //mul*vara^y=varb (mod nowp)
    int varq=int(ceil(sqrt(nowp))+0.5),per=1;
    Hs.Clear();
    tmp=varb%nowp;
    for(int i=0;i<varq;i++,tmp=1ll*tmp*vara%nowp)
        Hs.Insert(tmp,i),per=1ll*per*vara%nowp;
    tmp=mul%nowp;
    for(int i=0,res;i<=varq;i++,tmp=1ll*tmp*per%nowp)
        if(~(res=Hs.Query(tmp)) && i*varq-res>=0)
            return i*varq-res+add;
    return -1;
}
//BSGS找最小的解
void BSGS(int varp,int varn){
    varq=(int)(ceil(sqrt(varp))+0.5);
    int per=1;
    for(int i=0,tmp=varn;i<varq;i++,tmp=1ll*tmp*varb%varp)
        Hs.Insert(tmp,i),per=1ll*per*varb%varp;
    for(int i=0,tmp=1,res;i<=varq;i++,tmp=1ll*tmp*per%varp)
        if(~(res=Hs.Query(tmp)) && i*varq>=res){
            printf("%d\n",i*varq-res);
            return;
        }
    printf("no solution\n");
}

# 二次剩餘

- 引入問題

求解 \(x^2\equiv b\pmod p\)\(p\) 是一個奇素數。

若方程有解,則 \(b\) 稱爲 \(p\) 的二次剩餘

- 勒讓德符號

\[\left(\frac{b}{p}\right)=\begin{cases} 1&\text{b是p的二次剩餘}\\ -1&\text{b不是p的二次剩餘}\\ 0&b\equiv0 \end{cases} \]

- 一些引理

Lemma 1

$$\left(\frac bp\right)\equiv b^{\frac{p-1}2}$$

簡單證實一下:

  • \(b\equiv0\) 時顯然;
  • \(b\)\(p\) 的二次剩餘時,\(\exists x\in[0,p),x^2\equiv b\)
    由費馬小定理有 \(x^{p-1}\equiv1\),又有 \(p\) 是奇素數;
    能夠寫成 \((x^2)^{\frac{p-1}2}\equiv1\),則 \(b^{\frac{p-1}2}\equiv1\)
  • \(b\) 不是 \(p\) 的二次剩餘時,由逆元的惟一性可得 \(1,2,\cdots,p-1\) 能夠按照「\(i\)\(b\times i^{-1}\)\(i\) 的逆元)」的方式恰好劃分爲 \(\frac{p-1}2\) 組,每組乘積爲 \(b\)
    因而把 \(1,2,\cdots,p-1\) 所有乘起來就能夠獲得 \((p-1)!=b^{\frac{p-1}2}\),又有 \((p-1)!\equiv -1\)(威爾遜定理,不會 QwQ),則 \(b^{\frac{p-1}2}\equiv-1\)

Lemma 2

$p$ 剛好有 $\frac{p-1}2$ 個二次剩餘。

證實以下:設 \(x,y\) 知足 \(x^2\equiv y^2\)\(x< y\)\(x,y\in[1,p-1]\)),移項可得:

\[\begin{aligned} &(x-y)(x+y)\equiv 0\pmod p\\\Leftrightarrow\ &p\mid(x-y)(x+y) \end{aligned} \]

又有 \(p\) 是素數且 \(p\not\mid x-y\),則 \((p,x-y)=1\),進而可得 \(p\mid x+y\)。在 \([1,p-1]\) 中,這樣的 \((x,y)\)\(x^2\equiv y^2\) 一一對應

因此 \([1,p-1]\)恰有 \(\frac{p-1}2\) 個不一樣的 \(x^2\),即 \(\frac{p-1}2\) 個二次剩餘,剩下的 \(\frac{p-1}2\) 個數則是非二次剩餘。

因而有結論:\(p\)二次剩餘和非二次剩餘同樣多

Lemma 3

$$(a+b)^p\equiv a^p+b^p\pmod p$$

直接二項式展開,\(a^ib^{p-i}\) 項的係數爲 \(C_p^i\)

由於 \(p\) 是質數,因此當 \(i\neq 0,i\neq p\) 時,\(C_p^i=\frac{p!}{i!(p-i)!}\) 的分子是 \(p\) 的倍數而分母不是,因此 \(p\mid C_p^i\)

取模後只剩下首尾兩項。

- Cipolla

若是咱們知道 \(b\)\(p\) 的二次剩餘,如何求解?

能夠先隨機出一個 \(a\) 使得 \(a^2-b\) 不是 \(n\) 的二次剩餘,用勒讓德函數檢驗便可。因爲 \(p\) 的二次剩餘和非二次剩餘是同樣多的,因此能夠較快地隨機出這個 \(a\)

能夠知道不存在整數 \(x\) 使得 \(x^2=a^2-b\),因而咱們「擴展數域」,讓這樣的 \(x\) 存在——定義單位 \(\omega^2\equiv a^2-b\),相似於複數域,域中的全部數能夠表示爲 \(p+q\omega\) 的形式。

Theorem 5

$(a+\omega)^{\frac{p+1}2}$ 是原方程的一個根。

直接代入證實:

\[\begin{aligned} &(a+\omega)^{p+1}\\ =&(a+\omega)(a+\omega)^p\\ =&(a+\omega)(a^p+\omega^p) \end{aligned} \]

\(p\) 是奇素數,\(\omega^2\equiv a^2-b\),則

\[\omega^p=\omega\cdot(a^2-b)^{\frac{p-1}2} \]

由於 \(a^2-b\) 是非二次剩餘,因此 \((a^2-b)^{\frac{p-1}2}\equiv-1\)。又由費馬小定理,\(a^p\equiv a\),因此

\[a^p+\omega^p\equiv a-\omega \]

因此

\[\begin{aligned} &\Big[(a+\omega)^{\frac{p-1}2}\Big]^2\\ \equiv&(a+\omega)(a-\omega)\\ \equiv&a^2-\omega^2\\ \equiv&b \end{aligned} \]

因而 \(x_1=(a+\omega)^{\frac{p-1}2}\)。由 Lemma 2,另外一個根 \(x_2\) 知足 \(p\mid x_1+x_2\),則 \(x_2\equiv p-x_1\)

由於 \(p\) 是奇素數,\(x_1,x_2\) 必定奇偶性不一樣,則有兩個不等根。

- 參考實現3

> Link 洛谷 P5491

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long llong;
#define cint const int &

int varp,conw;

int Mul(cint a,cint b){return int(1ll*a*b%varp);}
int Add(cint a,cint b){return a+b>=varp? a+b-varp:a+b;}
int Sub(cint a,cint b){return a-b<0? a-b+varp:a-b;}
int Pow(cint a,cint b){return b? Mul(Pow(Mul(a,a),b>>1),(b&1)? a:1):1;}
struct COMPLEX{
	int conr,coni;
	COMPLEX(){}
	COMPLEX(cint varr,cint vari):conr(varr),coni(vari){}
	inline friend COMPLEX operator *(const COMPLEX &A,const COMPLEX &B){
		return COMPLEX(
			Add(Mul(A.conr,B.conr),Mul(Mul(A.coni,B.coni),conw)),
			Add(Mul(A.conr,B.coni),Mul(A.coni,B.conr))
		);
	}
	inline COMPLEX operator ^(int varb)const{
		COMPLEX vara=(*this),res(1,0);
		while(varb){
			if(varb&1) res=res*vara;
			vara=vara*vara;
			varb>>=1;
		}
		return res;
	}
};

//p is an odd prime
int Solve(int varn){
	varn%=varp;
	if(Pow(varn,(varp-1)/2)==varp-1) return -1;
	int vara,varw;
	while(true){
		vara=Mul(rand(),rand());
		varw=(Sub(Mul(vara,vara),varn));
		if(Pow(varw,(varp-1)/2)==varp-1) break;
	}
	conw=varw;
	return (COMPLEX(vara,1)^((varp+1)/2)).conr;
}
int main(){
	int cas;scanf("%d",&cas);
	while(cas--){
		int varn;scanf("%d%d",&varn,&varp);
		if(!varn){printf("0\n");continue;}
		int ans1=Solve(varn),ans2;
		if(~ans1){
			ans2=varp-ans1;
			if(ans1>ans2) swap(ans1,ans2);
			printf("%d %d\n",ans1,ans2);
		}
		else printf("Hola!\n");
	}
	return 0;
}

THE END

Thanks for reading!

\[\begin{split} 「\ &夢のなかを歩きまわる\\ &\quad\small{「 能一直走在夢想中}\\ &夜空を見上げて\ 星になるの\\ &\quad\small{請仰望星空\ 會成爲星星的}\\ &祈りの消えた街\ 夢のなかで\\ &\quad\small{在願望消失的街道上\ 在夢中}\\ &光の赤い夜寂れた心障\\ &\quad\small{在泛着赤紅色光芒的夜裏\ 心被寂悸囚禁}\\ &記憶の奧底で開いた壁\ 夢見て殺して\ 」\\ &\quad\small{夢想着完全衝破記憶深處的那副桎梏 」}\\ ——&\text{《餘命3日少女(Cover)》By 米白} \end{split} \]

> Linked 餘命3日少女-網易雲

相關文章
相關標籤/搜索