1. 程式人生 > 實用技巧 >數論概念總結

數論概念總結

關於數論

PART ONE 素數

1.1 素數

1.1.1 定義

素數(prime number)又稱質數,有無限個。

素數定義為在大於1的自然數中,除了1和它本身外不再有其他的因數,否則稱為合數。

1.1.2 性質

1、任何一個大於1的自然數都可以分解成幾個素數連乘積的形式,而且這種分解是唯一的。
大於1且第一個能被該自然數整除的數肯定是該分解中最小的素因子(唯一分解定理)。

2、兩個質數一定是互質數。例如,2與7、13與19。

3、一個質數如果不能整除另一個合數,這兩個數為互質數。例如,3與10、5與26。

4、1不是質數也不是合數,它和任何一個自然數在一起都是互質數。如1和9908。

5、相鄰的兩個自然數是互質數。如15與16。

6、相鄰的兩個奇數是互質數。如49與51。

7、2和任意奇數互質

1.1.3 求法

1、O(\(\sqrt{n}\))求單個素數

bool isPrime(int x) {
	if (x < 2) return false;
	for (int i = int(sqrt(x+0.5)); i >= 2; --i) {
		if (x % i == 0) return false;
	}
	return true;
}

2、O(n)線性篩求1~n之間的素數

int prime[MAXN]; // 儲存素數
bool is_not_prime[MAXN] = {1, 1}; // 0和1都不是素數
// 篩選 n 以內的所有素數
void xxs(int n) {
	for (int i = 2; i <= n; ++i) {
		if (!is_not_prime[i]) { // 如果i是素數
			prime[++prime[0]] = i;
		}
		for (int j = 1; j <= prime[0] && i * prime[j] <= n; ++j) {
			is_not_prime[i*prime[j]] = 1;
			// 如果i中包含了該質因子,則停止
			if (i % prime[j] == 0) break;
		}
	}
}

1.2 尤拉函式

1.2.1 尤拉

萊昂哈德·尤拉(Leonhard Euler ,1707年4月15日~1783年9月18日),瑞士數學家、自然科學家。1707年4月15日出生於瑞士巴塞爾,1783年9月18日於俄國聖彼得堡去世。歐拉出生於牧師家庭,自幼受父親的影響。13歲時入讀巴塞爾大學,15歲大學畢業,16歲獲得碩士學位。尤拉是18世紀數學界最傑出的人物之一,他不但為數學界作出貢獻,更把整個數學推至物理的領域。他是數學史上最多產的數學家,平均每年寫出八百多頁的論文,還寫了大量的力學分析學幾何學變分法等的課本,《無窮小分析引論》、《微分學原理》、《積分學原理》等都成為數學界中的經典著作。尤拉對數學的研究如此之廣泛,因此在許多數學的分支中也可經常見到以他的名字命名的重要常數、公式和定理。 [1] 此外尤拉還涉及

建築學彈道學航海學等領域。瑞士教育與研究國務祕書Charles Kleiber曾表示:“沒有尤拉的眾多科學發現,我們將過著完全不一樣的生活。”法國數學家拉普拉斯則認為:讀讀尤拉,他是所有人的老師。 [2] 2007年,為慶祝尤拉誕辰300週年,瑞士政府、中國科學院及中國教育部於2007年4月23日下午在北京的中國科學院文獻情報中心共同舉辦紀念活動,回顧尤拉的生平、工作以及對現代生活的影響。 [3]

1.2.2 定義

數論,對正整數n,尤拉函式是小於或等於n的正整數中與n互質的數的數目(因此φ(1)=1)。此函式以其首名研究者尤拉命名(Euler's totient function),它又稱為Euler's totient function、φ函式、尤拉商數等。 例如φ(8)=4,因為1,3,5,7均和8互質。 從尤拉函式引伸出來在環論方面的事實和拉格朗日定理構成了尤拉定理的證明。

1.2.3 性質

1、φ(1)=1(和1互質的數(小於等於1)就是1本身)。

注意:每種質因數只有一個。

2、若n是質數p的k次冪,

因為除了p的倍數外,其他數都跟n互質。

3、尤拉函式是積性函式——若m,n互質

4、當n為奇質數時,

5、p為素數,若 n%p=0,則\(\varphi(n\times p)=\varphi(n)\times p\)

6、p為素數,若 n% \(p\neq0\),則\(\varphi(n\times p)=\varphi(n)\times (p-1)\)

7、與n互質的數都是成對出現的,且每對的和為n,所以大於2的數的\(\varphi(n)\)都為偶數。

1.2.4 求法

1、單個尤拉函式值

int getphi(int xx){
    int ans=xx;
    int m=(int)sqrt(xx+0.5);
    for(int i=2;i<=m;i++){
        if(xx%i==0){
            ans=ans/i*(i-1);
            while(xx%i==0) xx/=i;
        }
    }
    if(xx>1) ans=ans/xx*(xx-1);
    return ans;
}

2、線性篩求1~n的尤拉函式值

void getphi(int n){
    phi[1]=1;
    for(int i=1;i<=n;i++){
        if(!isnot_prime[i]){
            prime[++prime[0]]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=prime[0] && i*prime[j]<=n;j++){
            isnot_prime[i*prime[j]]=1;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            } else {
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
}

PART TWO 最大公因數

2.1 最大公因數

2.1.1 定義

一組數的公約數,是指同時是這組數中每一個數的約數的數。而最大公約數,則是指所有公約數裡面最
大的一個,常縮寫為 gcd(Greatest Common Divisor)。
求 gcd 常用的方法為輾轉相除法(歐幾里得演算法),還有一種為更相減損法

2.1.2 求法

1、輾轉相除法(歐幾里得法)求兩個數的最大公因數

int getgcd(int aa,int bb){
    if(bb==0) return aa;
    return getgcd(bb,aa%bb);
}

2、輾轉相減法(尼考曼徹斯法)求兩個數的最大公因數

int getgcd(int aa,int bb){
    return aa==bb ? aa:getgcd(aa>bb ? aa-bb:aa, bb>aa ? bb-aa:bb);
}

如果寫高精度的話,用這一種會比較方便。

3、求兩個數的最小公倍數

\(lcm(aa,bb)=aa\times bb \div gcd(aa,bb)\)

4、求多個數的最大公因數或最小公倍數

兩兩相求就可以了

2.2 擴充套件歐幾里得定理

2.2.1 歐幾里得

歐幾里得(英文:Euclid;希臘文:Ευκλειδης ,約公元前330年—公元前275年),古希臘人數學家,被稱為“幾何之父”。他最著名著作幾何原本》是歐洲數學的基礎,提出五大公設,歐幾里得幾何,被廣泛的認為是歷史上最成功的教科書。歐幾里得也寫了一些關於透視圓錐曲線球面幾何學數論的作品。

2.2.2 定義

對於不完全為 0 的整數 a,b,gcd(a,b)表示 a,b 的最大公約數。那麼一定存在整數 x,y 使得 gcd(a,b)=ax+by。

2.2.3 求法

int exgcd(int aa,int bb,int &x,int &y){
    if(bb==0){
        x=1,y=0;
        return aa;
    }
    int ans=exgccd(bb,aa%bb,x,y);
    int t=x;
    x=y;
    y=t-aa/bb*y;
    return ans;
}

2.2.4 一般解

引用皎月半撒花大佬的證明

PART THREE 費馬小定理 & 尤拉定理

3.1 費馬小定理

3.1.1 費馬

皮埃爾·德·費馬,法國律師和業餘數學家。他在數學上的成就不比職業數學家差,他似乎對數論最有興趣,亦對現代微積分的建立有所貢獻。被譽為“業餘數學家之王”。費馬,是當今常見譯法,80年代的書籍文章也多見譯為“費爾瑪”的情況,但“費瑪”則少見。

3.1.2 定義

如果p是一個質數,而整數a不是p的倍數,則有a\(^{p-1}\)≡1(mod p)

另一個形式:若p為素數,對於任意整數 a,有a\(^{p}\) ≡ a(mod p)。

3.2尤拉定理

費馬小定理是用來闡述在素數模下,指數的同餘性質。當模是合數的時,就要應用範圍更廣的尤拉定理了。

3.2.1 定義

數論中,尤拉定理,(也稱費馬-尤拉定理)是一個關於同餘的性質。尤拉定理表明,若n,a為正整數,且n,a互質,則:

其實,當m為素數時,與費馬小定理相同

3.2.2擴充套件尤拉定理

PART FOUR 乘法逆元

4.1 乘法逆元

4.1.1 定義

若$(a \times x) $≡1(mod b),則稱 x 為 a在模 b意義下的乘法逆元,記為 \(a^{-1}\)
注意:並非所有的情況下都存在乘法逆元,但是當 gcd(a,b)=1即a,b互質時,存在乘法逆元

4.2 求法

4.2.1 費馬小定理求逆元

typedef long long ll;
ll quickpow(ll a, ll n, ll p) { //快速冪求 a^n % p
	ll ans = 1;
	while(n) {
		if(n & 1) ans = ans * a % p;
		a = a * a % p;
		n >>= 1;
	}
	return ans;
}
ll niyuan(ll a, ll p) { //費馬小定理求逆元 a^(p-2)%p
	return quickpow(a, p - 2, p);
}

注意:該方法的前提條件:模數為素數,且 a不是p的倍數。

4.2.2 尤拉定理求逆元

4.2.3 擴充套件歐幾里得求逆元

// ax+by=1
int exgcd(int a, int b, int &x, int &y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return b; // b為最大公約數
	}
	// 注意傳參及下面的計算
	int ret = exgcd(b, a%b, y, x);
	y -= a/b*x;
	return ret;
}
// ax+by=1
int exgcd2(int a, int b, int &x, int &y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return b; // b為最大公約數
	}
	// 下面的x、y交換是很直觀的根據推到式子來的
	int ret = exgcd2(b, a%b, x, y);
	int t = x;
	x = y;
	y = t - a/b*y;
	return ret;
}

4.2.4 線性求逆元

// 因為 1<i<p,所以 p/i 一定小於 p
ny[1] = 1;
for (int i = 2; i < p; ++i) {
	ny[i] = (long long)(p - p / i) * ny[p % i] % p; // 注意最後的模 p 不要忘記
}

注意:求逆元往往涉及大量的乘法,所以運算的時候一定要注意是否需要用到 long long。

4.3 總結

PART FIVE 組合數

5.1 定義

\(n\)個不同元素中,任取\(m(m≤n)\)個元素併成一組,叫做從\(n\)個不同元素中取出\(m\)個元素的一個組合;從\(n\)個不同元素中取出\(m(m≤n)\)個元素的所有組合的個數,叫做從\(n\)個不同元素中取出\(m\)個元素的組合數。

5.2 求法

5.2.1 公式法

計算組合數的一般公式:
\(C^m_n=\frac{n!}{m!(n-m)!}\)
其中\(n!=1\times2\times\cdots\times nn\)
特別地,定義\(0!=1\)
如果模數為質數,我們就可以提前處理出階乘的逆元和逆元的階乘

#define int long long
int getC(int n,int m){
	if(m==0) return 1ll;
	return jc[n]%mod*jcc[n-m]%mod*jcc[m]%mod;
}
signed main(){
    ny[1]=1;
    for(int i=2;i<=n;i++){
        ny[i]=(mod-mod/i)*ny[mod%i]%mod;
    }
    jc[0]=1;
    for(int i=1;i<=n;i++){
        jc[i]=jc[i-1]*i%mod;
    }
    jcc[0]=1;
    for(int i=1;i<=n;i++){
        jcc[i]=jcc[i-1]*ny[i]%mod;
    }
    int n,m;
    scanf("%lld%lld",&n,&m);
    printf("%lld\n",getC(n,m));
}

5.2.2 遞推法

針對大多數僅僅是利用組合數求解問題的題目運用遞推法打表,不僅方便,而且可以穩穩地控制複雜度,對於需要多次引用組合數的題目效果極佳:

基於組合數公理性質:\(C^m_n=C^{n-m}_n\)
(請大家務必記住此公式,由此在考場上靈活使用)

推得:\(C^m_n=C^{m-1}_{n-1}+C^m_{n-1}\)

由這個遞推公式就可以熟練的寫出組合數程式碼,但要注意初始化:

\(C^0_0=0\)

\(C^i_0=C^1_0=C^1_1=1\)( \(i\)為自然數 )

同時,把表打出來後,我們會發現———這就是楊輝三角,這個三角可以解決很多問題,記住列印三角的方法也可以打出組合數。

c[0][0]=c[1][0]=c[1][1]=1;
for(int i=2;i<=2000;i++){
	c[i][0]=1;
	for(int j=1;j<=i;j++){
		c[i][j]=c[i-1][j-1]+c[i-1][j];
	}
}