1. 程式人生 > 實用技巧 >【數論】數論相關口胡

【數論】數論相關口胡

正經人誰學數論啊

持續更新。
因為本人過於蒟蒻所以如果你想在這裡學些什麼的話還是算了
基本也就我自己看看

質數

Eratosthenes篩素數

其實就是劣質版線性篩,不過程式碼比較短,打個小表還是可以的。
時間效率\(O(nlog\ log\ n)\)

int Pri[maxn];
bool NotPri[maxn];
void JudPri(int n){
    NotPri[0]=1;
	NotPri[1]=1;//全域性變數大括號賦值可是很不好的習慣哦...
	for(int i=2;i<=n;i++){
		if(NotPri[i])continue;
		Pri[++Pri[0]]=i;
		for(int j=i;(long long)i*j<=n;j++)
		    NotPri[i*j]=1;
	}
}

線性篩素數

普通版

int Pri[maxn];
bool NotPri[maxn]={1,1};
void JudPri(int n){
    NotPri[0]=1;
	NotPri[1]=1;
	for(int i=2;i<=n;i++){
		if(!NotPri[i]){
			Pri[++Pri[0]]=i;
		}
		for(int j=1;j<=Pri[0]&&i*Pri[j]<=n;j++){
			NotPri[i*Pri[j]]=1;
			if(i%Pri[j]==0)break;
		}
	}
}

無需取模版

int v[maxn],Pri[maxn];
void JudPri(int n){
	for(int i=2;i<=n;i++){
		if(v[i]==0){
			v[i]=i;
			Pri[++Pri[0]]=i;
		}
		for(int j=1;j<=Pri[0];j++){
			if(i*Pri[j]>n||Pri[j]>v[i])break;
			v[i*Pri[j]]=Pri[j];
		}
	}
}

可能玄學上更快?(大霧)不過可能普通版的bool陣列更快也說不定呢(好像真的會快很多)。

Miller_Robbin大素數判定

(所以名字到底有幾個b?)
深入學習建議看Gary_818的部落格
是看臉的隨機化演算法,錯誤率基本趨近於0。
0202年了不會還有人\(O(\sqrt n)\)判斷素數吧不會吧不會吧
這種判斷素數的方法利用的是費馬小定理的逆命題,隨機列舉一個\(a\),滿足這個同餘式,那麼\(p\)就是素數。
不過其逆命題並不是個真命題(大霧),例如在\(p=341\)的時候若\(a=2\)滿足費馬小定理,然而341是一個合數(\(341=11\times 31\))。因此僅判斷一次得到的結果不一定正確,那怎麼增大正確率呢?
那就是重複判斷30次


時間複雜度\(O(log\ n)\)(忽略常數)

#include<bits/stdc++.h>
using namespace std;
const int count=30;
int n;

inline int qpow(int a,int b,int Mod){
    int ans=1,base=a;
    while(b){
        if(b&1)ans=ans*base%Mod;
		base=base*base%Mod;
		b>>=1;
    }
    return ans;
}

bool Miller_Rabbin(int n){
    if(n==2)
        return true;
    for(int i=1;i<=count;i++){
        int a=rand()%(n-2)+2;
        if(qpow(a,n,n)!=a)
            return false;
    }
    return true;
}

int main(){
    srand(time(0));
    scanf("%d",&n);
    if(Miller_Rabbin(n))
        printf("Probably a prime.");
    else
        printf("A composite.");
    printf("\n");
    return 0;
}

約數

GCD

輾轉相除法求GCD

各位dalao已經倒著都能寫了。
輾轉相除法求最大公約數。或者為了防止爆棧可以改成迴圈。

inline int gcd(int x,int y){
    if(y==0)return x;
    else return(y,x%y);
}

二進位制方法求GCD

如果想優化(可能大資料可以優化幾百\(\mathrm{ms}\))
就變成魔法少女就可以用二進位制優化(不過平時基本也無用

  • \(a\),\(b\)為偶數,則\(\gcd(a,b)=2\times \gcd(a/2,b/2)\)
  • \(a\)為奇數,\(b\)為偶數,則\(\gcd(a,b)=\gcd(a,b/2)\)
  • \(a\),\(b\)為奇數。假設\(a\geq b\),則\(\gcd(a,b)=\gcd((a-b)/2,b)\)
  • \(a\)\(0\),則返回\(b\)

可以用手寫abs和min卡常

inline ll abs(ll x){
    return x<0?-x:x;
}
inline ll min(ll a,ll b){
    return a<b?a:b;
}
inline ll gcd(ll a,ll b){
    if(a==0)return b;
    if(b==0)return a;
    if(!(a&1)&&!(b&1))return 2*gcd(a>>1,b>>1);
    else if(!(a&1))return gcd(a>>1,b);
    else if(!(b&1))return gcd(a,b>>1);
    else return gcd(abs(a-b),min(a,b));
}

沒有取模操作會快很多。

更相減損術求GCD

懶得寫,建議BFS。
高精度運算的時候可以用(不過既然都是要用高精度的題了為何不棄了呢

算數基本定理的推論

算數基本定理(唯一分解定理)

任意大於1的正整數\(N\)都可分解為有限個素數的乘積。
\(N=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\)
其中\(c_i\)都是正整數,\(p_i\)都是素數且滿足\(p_1<p_2<p_3⋯<p_m\)

求正約數個數

\(N\)的正約數個數為\(\prod\limits^m_{i=1}(c_i+1)\)

約數和定理

\(N\)的所有正約數之和為\(\prod\limits^m_{i=1}(\sum\limits^{c_i}_{j=0}(p_i)^j)\)

求正約數集合

一個大於1的正整數\(N\)的正約數集合可寫作

\[\{p_1^{b_1}p_2^{b_2}\dots p_m^{b_m}\},0\leq b_i\leq c_i \]

試除法

時間效率\(O(\sqrt n)\)。不會有人不會吧不會吧不會吧。

倍數法

倍數法以\(O(nlog\ n)\)的效率求出\(1-n\)所有數的正約數集合,比試除法的\(O(n\sqrt n)\)快很多。

vector<int> fac[maxn];
for(int i=1;i<=n;i++)
	for(int j=1;i*j<=n;j++)
		fac[i*j].push_back(i);

尤拉函式

\(1-N\)中與\(N\)互質的數的個數成為尤拉函式。

\[\varphi(n)=n \times \prod_{i=1}^k (1-\frac{1}{p_i}),p_i|n \]

性質

建議去看虎哥部落格(懶得粘

分解質因數求單個尤拉函式

這不是有手就行嗎

Eratosthenes篩求尤拉函式

妹想到吧又事它,時間效率\(O(nlog\ n)\),雖然效率不高但是很短(?)

int phi[maxn];
void Euler(int n){
	for(int i=2;i<=n;i++)
		phi[i]=i;
	for(int i=2;i<=n;i++)
		if(phi[i]==i)
			for(int j=i;j<=n;j+=i)
				phi[j]=phi[j]/i*(i-1);
}

線性篩求尤拉函式

這不是有手就行嗎

擴充套件歐幾里得

深入學習建議看YouXam的部落格
擴充套件歐幾里德定理(Extended Euclidean algorithm, EX(噁心)GCD)。
用於求\(ax+by=\gcd(a,b)\)的一組整數解。

\[ax_1+by_1=\gcd(a,b) \]

\[bx_2+(a\%b)y_2=gcd(b,a\%b) \]

\[x_1=y_2, y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2 \]

int exgcd(int a,int b,int &x,int &y) {
    if (b==0){
        x=1;
        y=0;
        return a;//a為最大公約數
    }
    int ret=exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return ret;
}

\(\gcd(a,b)=d\),對於更一般的方程\(ax+by=c\),它有解當且僅當\(d|c\)。可以先求出\(ax+by=d\)的一組特解\(x_0,y_0\),使其同時乘上\(\frac{c}{d}\),就得到了\(ax+by=c\)的一組特解\(\frac{c}{d}x_0,\frac{c}{d}y_0\)
\(ax+by=c\)通解可以表示為:

\[x=\frac{c}{d}x_0+k\frac{b}{d}\ ,\ y=\frac{c}{d}y_0-k\frac{a}{d}\ (k\in\mathrm{Z}) \]

同餘

費馬小定理

不是小費馬定理

前置芝士

剩餘類&剩餘系:總之大概就是一些數\(mod\)一個正整數\(n\)之後都能得到什麼結果(敷衍),最常用的完全剩餘系\(\{0,1,…,n-1\}\)

正文

\(p\)為素數,且\(a\)不是\(p\)的倍數則:

\[a^{p-1}\equiv 1(mod\ p) \]

另一個形式:\(p\)為素數,對於任意整數\(a\)

\[a^p\equiv a(mod\ p) \]

證明:不會。建議記住(霧)。

尤拉定理

當模數為合數的時候需要用範圍更廣泛的尤拉定理。
\(gcd(a,m)=1\),則:

\[a^{\varphi(m)}\equiv 1\ (mod\ m) \]

擴充套件尤拉定理

\[a^b\equiv \begin{cases} a^{b\ mod\ \varphi(p)}, & \gcd(a,p)=1 \\ a^b, & \gcd(a,p)\neq 1,b<\varphi(p)\pmod p \\ a^{b\ mod\ \varphi(p)+\varphi(p)}, & \gcd(a,p)\neq 1,b\geq\varphi(p) \end{cases}\]

證明
好像沒用過

乘法逆元

眾所周知同餘不滿足同除性,所以數學家們弄出了逆元這個東西。

費馬小定理求逆元

\(a^{p-2}\ mod\ p\)。快速冪求解,很方便。
前提條件:模數為素數,且\(a\)不是\(p\)的倍數。有的題特意卡這個,例如沙拉公主的困惑

尤拉定理求逆元

\(a^{\varphi(p)-1}\ mod\ p\)

擴充套件歐幾里得求逆元

前提條件:\(a\)\(p\)互質。

#include<bits/stdc++.h>
using namespace std;
int b,x,y,mod; 

inline int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1;
		y=0;
        return a;
	}
    int ret=exgcd(b,a%b,x,y);
    int t=x;
	x=y;
	y=t-(a/b)*y;
    return ret;
}

int main(){
    scanf("%d%d",&b,&mod);
    int gcd=exgcd(b,mod,x,y);
    if(gcd!=1)
        printf("None\n");
    else 
        printf("%d\n",(x%mod+mod)%mod);
    return 0;
}

線性求逆元

建議背式子

\[i^{-1} \equiv -\lfloor \frac{p}{i} \rfloor \times (p\ mod\ i)^{-1} \ (mod\ p) \]

inv[1]=1;
for(int i=2;i<=n;i++)
    inv[i]=(p-p/i)*inv[p%i]%p;

線性同餘方程

(蒟蒻表示並不會高次的)
暫時懶得寫了,下午填坑

中國剩餘定理

咕咕咕

莫比烏斯反演&&拉格朗日插值

建議問\(liuchanglc\)(滑稽)


震驚,\(\LaTeX\)竟如此難打!活到爆!