简单数论入门和基础数学知识(未完)
????????因為以前從來沒接觸過數論,小學也沒搞過奧數,而且數論這東西又挺重要的,不只是比賽,有些東西基本算是常識需要知道的。而且內容還很雜。所以決定好好寫一篇博客,包括知識點和自己的理解。
目錄
0.特殊性質
一.分解質因數和算術基本定理
如何分解質因數
二.判斷素數 和素數篩(埃篩和歐拉篩)
素數判斷
素數篩法
三.約數
一個數的所有約數
約數個數?
約數之和
最大公約數GCD
四.歐拉函數和歐拉函數篩及歐拉定理
求一個數的歐拉函數
歐拉函數篩
歐拉定理
費馬小定理
0.特殊性質
平常寫題會碰到的一些特殊性質。
整除性質:
1、若b|a,c|a,且b和c互質,則bc|a。
2、對任意非零整數a,±a|a=±1。
3、若a|b,b|a,則|a|=|b|。
4、如果a能被b整除,c是任意整數,那么積ac也能被b整除。
5、如果a同時被b與c整除,并且b與c互質,那么a一定能被積bc整除,反過來也成立。
6、對任意整數a,b>0,存在唯一的數對q,r,使a=bq+r,其中0≤r<b,這個事實稱為帶余除法定理,是整除理論的基礎。
7、公因數一定整除最大公因數。
8.d|a,d|b 則d|ax+by。
9.整除的傳遞性。若a|b b|c 則a|c。
1.定理:兩數最大公約數與最小公倍數的積等于兩數之積?
所以互質的兩數乘積就是最小公倍數。
2.
一.分解質因數和算術基本定理
? ? ? ?① 惟一分解定理 (算術基本定理)?可表述為:任何一個大于1的自然數?N,如果N不為質數,那么N可以唯一分解成有限個質數的乘積N=,這里P1<P2<P3......<Pn均為質數,其中指數ai是正整數。這樣的分解稱為?N?的標準分解式。
? ? ? ? ②質因數(素因數或質因子)在數論里是指能整除給定正整數的質數。除了1以外,兩個沒有其他共同質因子的正整數稱為互質。因為1沒有質因子,1與任何正整數(包括1本身)都是互質。正整數的因數分解可將正整數表示為一連串的質因子相乘,質因子如重復可以用指數表示。根據算術基本定理,任何正整數皆有獨一無二的質因子分解式??。只有一個質因子的正整數為質數。
如何分解質因數
分解質因數,也就是將n表示為標準分解式。所以我們要求出質因數和對應的指數。
試除法:
為什么只要枚舉到sqrt(n)就行呢?
因為超過sqrt(n)的部分基本沒有意義,因為如果存在多個大于sqrt(n)的質因數,根據惟一分解定理,n由這些數相乘組成,那么多個大于sqrt(n)的質因數相乘就超過了n,矛盾。
所以最多只會有一個大于sqrt(n)的質因數,所以當枚舉到sqrt(n)時剩下的數不等于1,那么說明還剩下這個大于sqrt(n)的質因數。額外加上即可。
vector<pair<int,int>> div(int n){vector<pair<int,int>> ans; //因為惟一分解定理,合數也是由多個質數組成,合因數組成的較小質數會被先分解,所以每次得到的必定是質因數for(int j=2;j<=n/j;j++){//除凈小于sqrt(n)的質因數int sum=0;while(n%j==0){//除凈質因數n/=j;sum++;//該質因數的指數}if(sum!=0)ans.push_back({j,sum});}if(n>1)//大于sqrt(n)的質因數只會有一個,如果有多個,分解定理的乘積就大于n了ans.push_back({n,1});return ans; }unordered_map<int,int> div(int n){//得到質因數unordered_map<int,int>ans;for(int j=2;j<=n/j;j++){if(n%j==0){while(n%j==0){n/=j;ans[j]++;} }}if(n>1)ans[n]++;return ans; }參考:
數學數論相關 - ternary_tree 的博客 - 洛谷博客 (luogu.org)
二.判斷素數 和素數篩(埃篩和歐拉篩)
素數判斷
·????????根據素數的定義,只含有1和本身的因數的數被稱為素數,由此定義枚舉小于這個數的所有數,如果出現能夠整除的,說明這個數不是質數。
注意:此處只需要枚舉到sqrt(k)。
因為 若 i | k,則 k/i | k
所以i如果不是因數,那么k/i也不是,最多到sqrt(k),超過這個范圍就沒有意義了。
O(sqrt(n))
此處兩數相乘采用逆元,可以防止爆int
int is_prime(int k) {//判斷k是否是質數int ok = 1;if (k < 2)return 0;for (int j = 2; j <= k / j; j++) {//枚舉到sqrt(k)if (k % j == 0) {ok = 0;return 0;}}return 1; }?素數篩法
P3383 【模板】線性篩素數 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn)
? ? ? ? 1.埃篩的思想比較簡單,一個數的所有倍數都不可能是質數所以,枚舉當前數的范圍內所有倍數,將他們刪去,剩下的都是質數。同時可以簡單優化一下,由惟一分解定理,只需要篩去質數,合數也就會被順帶篩去。所以枚舉質數的倍數即可。優化后復雜度O(nloglog)
? ? ? ? 2.歐拉篩也叫做線性篩。他保證每次篩的時候只會通過一個質因數將一個合數篩去,而不像埃篩那樣重復篩同一個數好多次。
其主要的優化有兩方面:
①每次只枚舉質數的倍數。
②保證prime[i]是某個被篩去的合數的最小質因數.
重點在第二點。因為我們是從小到大枚舉的質數,那么當第一次滿足判斷的數j,j%prime[i]==0那么這個prime[i]就是j的最小質因數,同時也是prime[i]*j的最小質因數。同時,因為prime[i]是j的最小質因數,那么prime[i]也會是之后prime[i+1~size]*j這些合數的最小質因數。
原理:歐拉篩篩素數 - 學委 的博客 - 洛谷博客 (luogu.com.cn)
#include<bits/stdc++.h> using namespace std; #pragma warning(disable:4996); //#define int long long #define rep(j,b,e) for(int j=(b);j<=(e);j++) #define drep(j,e,b) for(int j=(e);j>=(b);j--) const int N = 1e8 + 10; int T = 1; int n, m, k; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b); } vector<int>prime; int NotisPrime[N];//1表示不是一個質數 void GetPrime_Era(int n) {//會多次標記同一個值rep(j, 2, n) {if (NotisPrime[j] == 0) {//如果是質數prime.push_back(j);for (int i = j; i <= n/j; i++)//質數的倍數全是合數,逆元寫成/j防止爆intNotisPrime[i * j] = 1;}} } void GetPrime_Ola(int n) {//效率更高,一個數只會被他的最小質因數篩掉rep(j, 2, n) {if (NotisPrime[j] == 0)//篩完還是素數就加入prime.push_back(j);for (int i = 0; i < prime.size() && prime[i] <= n/j; i++) {//逆元枚舉質數,篩pi*jNotisPrime[j * prime[i]] = 1;if (j % prime[i] == 0)break;//保證prime[i]是這個合數的最小質因數}} } signed main() { #ifndef ONLINE_JUDGEfreopen("out.txt", "w", stdout); #endifios::sync_with_stdio(0); cout.tie(0);int q;cin >> n >> q;GetPrime_Ola(n);rep(j, 1, q) {cin >> k;cout << prime[k - 1] << endl;} }三.約數和公約數
一個數的所有約數
試除法
枚舉到sqrt(n) ,原理同判斷質數。
vector<int> div(int k){vector<int>ans;for(int j=1;j<=k/j;j++){if(k%j==0){ans.push_back(j);if(j!=k/j)ans.push_back(k/j);}}sort(ans.begin(),ans.end());//k的約數約有log個return ans; }約數個數?
870. 約數個數 - AcWing題庫
本題是求一個很大乘積的約數,而這個很大的數不比表示出來,他的標準分解式就等于每個乘數的標準分解式的乘積。
由算術基本定理:
N=
一個數的約數個數 sum=?,也就是質因數指數+1的乘積。
因為算術基本定理,里面的質因數隨意組合得到的數都是N的約數,每個質因數有0-ai種選法,由乘法原理,總的選擇數為指數+1的乘積。
做法:
先對數N進行質因數分解,求出質因數和對應指數,然后求指數+1的乘積即可。
#include<bits/stdc++.h> using namespace std; const int mod=1e9 +7; int main(){int n;cin>>n;unordered_map<int,int>prime;//質因數和指數while(n--){int k;cin>>k;for(int j=2;j<=k/j;j++){//分解質因數得到底數和指數while(k%j==0){k/=j;prime[j]++;}}if(k>1)prime[k]++;}long long ans=1;for(auto [p,cnt]:prime){ans=(ans*(cnt+1))%mod;//約數個數等于分解式的所有指數+1的乘積}cout<<ans<<endl; }約數之和
871. 約數之和 - AcWing題庫
根約數個數的推導類似,所有質因數及其指數的排列組合構成所有的因數,所以約數和就是將每種組合都列出來然后相加,而提取公因式后可以得到
sum=
也就是所有質因數的所有指數的和的乘積
所以做法和約數個數很像,先分解質因數求出質因數和指數,然后寫出公式。
這里求一個質因數的所有指數的和有個簡便方法。
迭代 t=p*t+1
#include<bits/stdc++.h> using namespace std; const int mod=1e9 +7; int main(){int n;cin>>n;unordered_map<int,int>prime;//質因數和指數while(n--){int k;cin>>k;for(int j=2;j<=k/j;j++){//分解質因數得到底數和指數while(k%j==0){k/=j;prime[j]++;}}if(k>1)prime[k]++;}long long ans=1;for(auto [p,cnt]:prime){long long t=1;while(cnt--)t=(p*t+1)%mod;//質因數所有指數的和ans=(ans*t)%mod;//求積}cout<<ans<<endl; }最大公約數GCD
輾轉相除法:
如果
?且??則有
所以對于A和B的最大公約數,則會滿足? ?用取模加快減法運算
得到
int gcd(int a,int b){return b==0?a:gcd(b,a%b); }?裴蜀定理
設??是不全為零的整數,則存在整數?, 使得?.
且gcd(a,b)是能得到的最小值,其他值全是gcd的倍數。
擴展歐幾里得算法
877. 擴展歐幾里得算法 - AcWing題庫
擴展歐幾里得算法是用來求解裴蜀定理中的x和y的。
好煩不會證明qwq
#include <iostream> #include <cstring> #include <algorithm>using namespace std; //a*b+b*y=gdc(a,b) int exgcd(int a,int b,int& x,int& y){if(b==0){x=1,y=0;return a;}int res=exgcd(b,a%b,y,x);y-=a/b*x;return res; } int main() {int n;cin>>n;while (n -- ){int a,b,x,y;cin>>a>>b;exgcd(a,b,x,y);cout<<x<<" "<<y<<endl;} }?線性同余方程
878. 線性同余方程 - AcWing題庫
線性同余方程:
得
由擴展歐幾里得,可得
的x,
又因為gcd(a,p) | (ax+by)
所以,如果b是gcd(a,p)的倍數,方程有解且解為方程的倍,反之無解。
所以先用gcd判斷有解后,使用擴展歐幾里得算法得到,然后擴大倍即可
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long LL;int n; int a,b,p; int exgcd(int a,int b,int &x,int &y){if(b==0)//mod等于0相當于整除{//a*1+b*0=ax=1,y=0;return a;}int d=exgcd(b,a%b,y,x);y-=a/b*x;return d; } int main() {cin >> n;while (n -- ){cin>>a>>b>>p;int x,y;int d=exgcd(a,p,x,y);//a*x=p*y+bif(b%d)cout<<"impossible"<<endl;else{x=(LL)x*(b/d)%p;cout<<x<<endl;}} }四.歐拉函數和歐拉函數篩及歐拉定理
求一個數的歐拉函數
873. 歐拉函數 - AcWing題庫
?與N互質的數的個數稱為N的歐拉函數,記做phi[N]
歐拉函數值可以通過上表的公式得到,可以看到N的歐拉函數只與質因數有關,和指數無關。
證明依靠容斥定理,略。
可以做法就是先求出N的所有質因數,然后帶入公式
#include <iostream> #include <cstring> #include <algorithm> using namespace std; unordered_map<int,int> div(int n){//得到質因數unordered_map<int,int>ans;for(int j=2;j<=n/j;j++)if(n%j==0)while(n%j==0){n/=j;ans[j]++;} if(n>1)ans[n]++;// 惟一一個大于sqrt(n) 的質因數不要遺漏return ans; } int main() {int n;cin >> n;while (n -- ){int k;cin >> k;auto ans=div(k);int res=k;for(auto&[prime,cnt]:ans){res=res/prime*(prime-1);//帶入公式,注意都是整除??梢韵瘸齪rime,因為prime是質因數,N能正常prime,然后再乘prime-1保證精度不丟失}cout<<res<<endl;} }歐拉函數篩
874. 篩法求歐拉函數 - AcWing題庫
可以根據線性篩的思路,得到質數,根據質數作為某個數的質因數進行篩。
根據公式可以推出,
①j為質數數,歐拉函數ola[j]=j-1,因為質數的質因子只有他本身,也可以認為質數之前的所有數都和他自己互質(公約數只為1)
②pi為質數,j為任意數,且pi是j的質因子
根據歐拉公式,pi*j和 j 的質因數無差別,只有pi的指數有差別歐拉函數和質因子指數無關,所有歐拉函數的差別僅在N上。
易得 ola[pi*j]=pi*ola[j]
③pi為質數,j為任意數,且pi不是j的質因子
根據歐拉公式,pi*j和j的標準分解式差距只在pi*j多一個pi。帶入歐拉公式
多一個pi*(pi-1)/pi=pi-1
易得 ola[pi*j]=(pi-1)*ola[j]
#include <bits/stdc++.h>using namespace std; int n; const int N = 1e6+10; vector<int>prime; int NotIsPrime[N]; long long ola[N]; long long get_ola(int n){ola[1]=1;//1的歐拉函數為1for(int j=2;j<=n;j++){if(NotIsPrime[j]==0){prime.push_back(j);ola[j]=j-1;//質數前面的所有數都與他互質}for(int i=0;i<(int)prime.size() and prime[i]<=n/j;i++){NotIsPrime[prime[i]*j]=1;if(j%prime[i]==0){ola[j*prime[i]]=prime[i]*ola[j];//pi是j的最小質因數,歐拉函數和質因數指數無關,大小只變化pi倍break;}else{ola[j*prime[i]]=ola[j]*(prime[i]-1);;//*prime[i]-1;//根據歐拉函數公式,乘上非質因數之后變化pi*((pi-1)/pi),也就是pi-1倍}}}return accumulate(ola,ola+n+1,0ll); } int main() {cin>>n;cout<<get_ola(n); }歐拉定理
若 a和b互質,則?
證明:
設為與b互質的數的集合。
給集合每個數乘上一個a得到集合
,集合元素相乘,提取a,同時mod b
消去相同部分
費馬小定理
由歐拉定理,當b為質數時phi[b]=b-1
五.快速冪
快速冪的本質思路還是倍增,將冪表示為二進制即用二進制表示所有數。
例如3^5,5的二進制 101,可以看作2^0+2^2所以原式可以寫成?
所以用一個數存儲,到當前位是2的幾次方,如果該位為1,說明需要乘上。
注意快速冪防止爆int須先轉為long long在進行運算,不能運算完再強轉
int qpow(int a,int n){int ans=1;while(n){if(n&1==1)ans=((ll)ans*a)%mod;//當前位為1n>>=1;//移位a=((ll)a*a)%mod;//當前位是2的幾次方}return ans%mod; }?快速冪求逆元
876. 快速冪求逆元 - AcWing題庫
也就是b與p互質(gcd(b,p)==1且保證同余p結果相同的情況下,用a乘上x表示a/b。x就叫b的逆元?。
由
將
兩邊同乘b,除a
得
?由費馬小定理,如果p為質數時
所以b與p互質且p為質數時逆元x=
又因為p為質數,b要與p互質只需要b不是p的整數倍即可。
所以p為質數時求b的逆元等效于求
使用快速冪
#include<bits/stdc++.h> using namespace std; int n; int a,p; #define ll long long ll qpow(int a,int b){ll ans=1;while(b){if(b&1)ans=(ll)ans*a%p;b>>=1;a=(ll)a*a%p;}return ans%p; } int gcd(int a,int b){return b==0?a:gcd(b,a%b); } int main(){cin>>n;while(n--){cin>>a>>p;int res=qpow(a,p-2);if(a%p!=0){//(gcd(a,p)==1.p為質數,其他數若與他互質,只能不是p的整數倍cout<<res<<endl;}elsecout<<"impossible\n";} }總結
以上是生活随笔為你收集整理的简单数论入门和基础数学知识(未完)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【MapGIS精品教程】005:MapG
- 下一篇: 蒙特卡洛模拟 matlab实例,蒙特卡洛