博主是個數學菜雞,它考試幾乎沒及格過,但是他牛逼的同學們要他寫筆記,so,他只能硬着頭皮屑了,咕咕咕,可能有很多錯誤還望海涵!
有關素數的小結
質數(prime number)又稱素數,有無限個。一個大於1的自然數,除了1和它本身外,不能被其他自然數整除,換句話說就是該數除了1和它本身以外不再有其他的因數;否則稱為合數。來自360百科
素數好玩的性質:
- 存在任意長的一段連續數,其中的所有數都是合數(相鄰素數之間的間隔任意大)
證明:當\(0<a<=n\)時,\(n!+a\)能被a整除。長度為n-1的數列\(n!+2, n!+3, n!+4, …, n!+n\)中,所有的數都是合數。這個結論對所有大於1的整數n都成立,而n可以取到任意大。
- 所有大於\(2\)的素數都可以唯一地表示成兩個平方數之差。
證明:大於\(2\)的素數都是奇數。假設這個 數是\(2n+1\)。由於\((n+1)^2=n^2+2n+1\),\((n+1)^2\)和\(n^2\)就是我們要找的兩個平方數。下面證明這個方案是唯一的。如果素數p能表示成 \(a^2-b^2\),則\(p=a^2-b^2=(a+b)(a-b)\)。由於p是素數,那么只可能\(a+b=p\)且\(a-b=1\),這給出了a和b的唯一解。
- 當n為大於2的整數時,\(2^n+1\)和\(2^n-1\)兩個數中,如果其中一個數是素數,那么另一個數一定是合數。
證明:\(2^n\)不能被3整除。如果它被3除余1,那么\(2^n-1\)就能被\(3\)整除;如果被\(3\)除余2,那么\(2^n+1\)就能被\(3\)整除。總之,\(2^n+1\)和\(2^n-1\)中至少有一個是合數。
證明素數無限多
先來個簡單的:
2是素數,23是素數,233是素數,2333是素數,23333是素數,233333是素數,2333333是素數,23333333是素數,233333333是素數........
其實以上是放屁....
正確的證明:
經典反證法:
假設素數是有限的,假設素數只有有限的\(n\)個,最大的一個素數是p
設q為所有素數之積加上1,那么,\(q = ( 2 * 3 * 5 * …… * p )+ 1\)不是素數
那么,q可以被\(2、3、……p\)中的數整除
而q被這\(2、3、……、p\)中任意一個整除都會余1,與之矛盾
所以,素數是無限的.
素數計數函數:
好想沒什么用,看看就行了...咕咕咕
小於或等於 \(x\) 的素數的個數,用 \(\pi(x)\) 表示。隨着 \(x\) 的增大,有這樣的近似結果: \(\pi(x) \sim \frac{x}{\ln(x)}\)
素數判定
暴力做法
自然可以枚舉從小到大的每個數看是否能整除
bool Prime(int x) {
if (x < 2) return 0;
for (int i = 2; i < x; ++i)
if (x % i == 0)
return 0;
return 1;
}
小優化
只需要循環到\sqrt{a},因為因子是成對出現的
bool iPrime(int x) {
if (x < 2) return 0;
for (int i = 2; i * i <= x; ++i)
if (x % i) return 0;
return 1;
}
要寫i*i<=x,而不是i<=sqrt(x);
因為sqrt比直接乘要慢的多
Miller-Rabin 素性測試
其實我也不會,你可以去看看lyq大佬的博客,順便膜拜她!
我就只給出代碼了,咕咕咕,其實代碼是來自大佬lzx的,我太弱了
我只知道該方法的原理是利用二次探測定理和飛馬小定理來隨機生成幾個a利用費馬小定理和二次探測定理來檢測素數
如果p是一個素數,且0<x<p,則方程x*x≡1(mod p)的解為x=1,p-1.
#include <bits/stdc++.h>
#define int long long
using namespace std;
int test[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int n, m;
template<class T> inline void read(T &x) {
x = 0;int f = 0;char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'),ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
x = f ? -x : x;
return ;
}
int qpow(int a, int b, int p) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % p;
a = 1ll * a * a % p, b >>= 1;
}
return ans;
}
bool miller_rabin(int p) {
if(p == 1) return 0;
int t = p - 1, k = 0;
while(!(t & 1)) k++, t >>= 1;
for(int i = 0; i <8; ++i) {
if(p == test[i]) return 1;
long long a = qpow(test[i], t, p), nx = a;
for(int j = 1; j <= k; ++j) {
nx = (a * a) % p;
if(nx == 1 && a != 1 && a != p - 1) return 0;
a = nx;
}
if(a != 1) return 0;
}
return 1;
}
signed main() {
cin>>n>>m;
long long ans=0;
for (int i = n, x; i <= m; ++i) {
if(miller_rabin(i)) ans++;
}
printf("%lld",ans);
return 0;
}
Fermat 素性測試
原理:
假如p是質數,且\(gcd(a,p)=1\),那么 \(a(p-1) ≡1(mod p)\)。即:假如a是整數,p是質數,且a,p互質(符號不會打湊合着看吧),那么\(a^(p-1)/p\)的余數恆等於1。
思路:那么,根據費馬小定理的逆定理,如果們找到一個 a ,使得 a ^(n-1)%n !=1,是否就可以確定數字 n 不是質數呢?很遺憾,這是不行的,費馬小定理的逆定理並不正確。
偽素數:
滿足2^(n-1)%n1的合數 n 。
滿足a^(n-1)%n1的合數n叫做以 a 為底的偽素數。
Carmichael數:對於所有小於 n 的底數 a,都滿足a^(n-1)%n==1 的數。(前10億個數中,僅有600多個這樣的數存在)。
在具體實現用fermat素性測試中,我們一般是隨機選取有限個數的底數 a ,對 n 進行素性測試。若全部滿足,則認為數字 n 是質數,若有一個不滿足,則認為數字 n 不是質數。
以上部分內容參考百度
eratosthenes篩選法(埃拉托斯特尼篩法)
原理:素數的倍數是合數
步驟:
1.列出2,3,4..........100
2.選取第一個沒被處理過的數(第一次為2)
3.去掉以2為因數的所有數
4.如果下一個素數小於sqrt(100)跳轉到第二步
代碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string>
#include<cstring>
#define ll long long int
#define MAXN 100000
using namespace std;
const int maxn=999999999;
const int minn=-999999999;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int prime[MAXN],n;
int main()
{
cin>>n;
for(int i=2;i<=n;++i)
{
if(prime[i]) continue;
cout<<i<<" ";
for(int j=1;j<=n/i;++j) prime[i*j]=1;
}
return 0;
}
歐拉篩
如果能讓每個合數都只被標記一次,那么時間復雜度就可以降到 \(O(n)\) 了,所以對上面的方法進行優化,保證每個合數被最小質因子篩掉
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string>
#include<cstring>
#define ll long long int
#define MAXN 10000
using namespace std;
const int maxn=999999999;
const int minn=-999999999;
inline int read() {
char c = getchar();
int x = 0, f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int v[MAXN]/*最小質因子*/,prime[MAXN],m/*質數數量*/;
int main() {
int n;
cin>>n;
for(int i=2; i<=n; ++i) {
if(v[i]==0) {
v[i]=i;
prime[++m]=i;
}
for(int j=1; j<=m; ++j) {
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
for(int i=1; i<=m; ++i) {
cout<<prime[i]<<endl;
}
return 0;
}
費馬查表法
這個是真的不會了,所一就放張圖片吧
代碼:
public void femat(int n){
for(int i = 2; i <= n; i++){
if(runFermatPower(2, n-1, n) != 1){
if(i不在偽素數表中)
輸出i;
}
}
}
有關gcd和lcm的小結
gcd遞歸定理(歐幾里得)
證明:
我們假設\(a>b\)
設 \(a=bk+c\) ,顯然有 \(c=a \bmod b\) 。設 \(d|a\) \(d|b\) ,則 \(c=a-bk\) \(\frac{c}{d}=\frac{a}{d}-\frac{b}{d}k\), 由右邊的式子可知 \(\frac{c}{d}\) 為整數,即 \(d|c\) 所以對於 \(a,b\) 的公約數,它也會是 \(a \bmod b\) 的公約數。
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
算術基本定理
每一個正整數都可以表示成若干整數的乘積,這種分解方式在忽略排列次序的條件下是唯一的。
即:
設 \(a = p_{a_1}^{k_{a_1}}p_{a_2}^{k_{a_2}} \cdots p_{a_s}^{k_{a_s}}\) , \(b = p_{b_1}^{k_{b_1}}p_{b_2}^{k_{b_2}} \cdots p_{b_s}^{k_{b_s}}\)
我們發現,對於 \(a\) 和 \(b\) 的情況,二者的最大公約數等於
\(p_1^{k_{\min(a_1, b_1)}}p_2^{k_{\min(a_2, b_2)}} \cdots p_s^{k_{\min(a_s, b_s)}}\)
最小公倍數等於
\(p_1^{k_{\max(a_1, b_1)}}p_2^{k_{\max(a_2, b_2)}} \cdots p_s^{k_{\max(a_s, b_s)}}\)
由於 \(a + b = \max(a, b) + \min(a, b)\)
所以得到結論是 \(\gcd(a, b) \times \operatorname{lcm}(a, b) = a \times b\)
lcm(a,b)的求法
先求出\(gcd(a,b)\),然后根據\(gcd(a,b) \times lcm(a,b)=a*b\)求解
擴展歐幾里得-exgcd()
求 \(ax+by=\gcd(a,b)\) 的一組可行解
證明
\(ax_1+by_1=\gcd(a,b)\)
\(bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b)\)
由歐幾里得定理可知: \(\gcd(a,b)=\gcd(b,a\bmod b)\)
\(ax_1+by_1=bx_2+(a\bmod b)y_2\)
\(a\bmod b=a-(\lfloor\frac{a}{b}\rfloor\times b)\)
所以 \(ax_1+by_1=bx_2+(a-(\lfloor\frac{a}{b}\rfloor\times b))y_2\)
\(ax_1+by_1=ay_2+bx_2-\lfloor\frac{a}{b}\rfloor\times by_2=ay_2+b(x_2-\lfloor\frac{a}{b}\rfloor y_2)\)
因為 \(a=a,b=b\) ,所以 \(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2\)
來自百度
exgcd的求法:
就直接套gcd遞歸定理就行了 在這個過程中計算 \(x,y\) 即可
代碼解釋請左轉百度
int Exgcd(int a, int b, int &x, int &y) {//此處也可用全局變量
if (!b) {
x = 1;
y = 0;
return a;
}
int d = Exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return d;
}
費馬小定理和歐拉定理
話說上面好像用到了,怎么到現在才寫,咕咕咕
歐拉定理
若 \(\gcd(a, m) = 1\) ,則 \(a^{\phi(m)} \equiv 1 \pmod{m}\) 。
證明:
將\(1~n\)中與\(n\)互質的數按順序排布:$x1,x2……xφ(n) \((顯然,共有\)φ(n)$個數)
我們考慮這么一些數:
\(m1=a*x1;m2=a*x2;m3=a*x3……mφ(n)=a*xφ(n)\)
(1)
這些數中的任意兩個都不模n同余,因為如果有\(mS≡mR (mod n)\) (這里假定mS更大一些),就有:
\(mS-mR=a(xS-xR)=qn\),即n能整除\(a(xS-xR)\)。但是\(a\)與\(n\)互質,a與n的最大公因子是1,而xS-xR<n,因而左式不可能被n整除。也就是說這些數中的任意兩個都不模n同余,\(φ(n)\)個數有\(φ(n)\)種余數。
(2)
這些數除n的余數都與n互質:
我們知道\(a\),\(xi\)與\(n\)互質,則\(a × xi\) 與n互質,根據歐幾里得算法,則\(n\)與\((a × xi) %n\)也互質 。
那么這些數除n的余數,都在\(x1,x2,x3……xφ(n)\)中,因為這是\(1~n\)中與\(n\)互質的所有數,而余數又小於\(n.\)
由(1)和(2)可知,數\(m1,m2,m3……mφ(n)\)(如果將其次序重新排列)必須相應地同余於\(x1,x2,x3……xφ(n)\)
故得出:\(m1*m2*m3……mφ(n)≡x1*x2*x3……xφ(n) (mod n)\)
或者說\(a^[φ(n)]*(x1*x2*x3……xφ(n))≡x1*x2*x3……xφ(n)\)
或者為了方便$K{a[1]-1}≡0 ( mod n ) \(這里\)K=x1x2x3……xφ(n)$
可知\(K{a^[φ(n)]-1}\)被\(n\)整除。但K中的因子\(x1,x2……\)都與\(n\)互質,所以\(K\)與\(n\)互質。那么\(a^[φ(n)]-1\)必須能被n整除,即\(a^[φ(n)]-1≡0 (mod n)\),即\(a^[φ(n)]≡1 (mod n)\),得證。
此證明是lhy教的
歐拉定理求法:
先寫暴力求歐拉函數
nt Eular(int m)
{
int ret = m;
for(int i=2; i<m; i++)
{
if(m%i == 0)
ret -= ret/i;
while(m%i == 0)
{
m /= i;
}
}
if(m > 1)
ret -= ret/m;
return ret;
}
顯然復雜度太高了,基本TLE 預定
所以要用線性\(o(n)\)來篩。
證明
\(phi(p) == p-1\) 因為素數\(p\)除了\(1\)以外的因子只有\(p\),所以與$ p$ 互紙的個數是 \(p - 1\)個
\(phi(p^k) == p^k - p^(k-1) == (p-1) * p^(k-1)\)
證明:
令\(n == p^k\),小於$ n \(的正整數共有\) p^k-1$ 個,其中與$ p \(不互質的個數共\) p^(k-1)-1 \(個,它們是\) 1p,2p,3p ... (p^(k-1)-1)p$
所以\(phi(p^k) == (p^k-1) - (p^(k-1)-1) == p^k - p^(k-1) == (p-1) * p^(k-1)\)
如果$i mod p == 0, 那么 phi(i * p) == p * phi(i) $
舉個例子:
假設$ p = 3,i = 6,p * i = 18 = 2 * 3^2$
\(phi(3 * 6) == 18*(1-1/2)*(1-1/3) = 6\)
$p * phi(i) = 3 * phi(6) = 3 * 6 * (1-1/2) * (1-1/3) = 6 = phi(i * p) $正確
如果\(i mod p != 0\) 那么 $phi(i * p) == phi(i) * (p-1) $
oi_wiki的證明:
注意到在線性篩中,每一個合數都是被最小的質因子篩掉。比如設 \(p_1\) 是 \(n\) 的最小質因子, \(n' = \frac{n}{p_1}\) ,那么線性篩的過程中 \(n\) 通過 \(n' \times p_1\) 篩掉。
觀察線性篩的過程,我們還需要處理兩個部分,下面對 \(n' \bmod p_1\) 分情況討論。
如果 \(n' \bmod p_1 = 0\) ,那么 \(n'\) 包含了 \(n\) 的所有質因子。
那如果 \(n' \bmod p_1 \neq 0\) 呢,這時 \(n'\) 和 \(n\) 是互質的,根據歐拉函數性質,我們有:
證明:
i mod p 不為0且p為質數, 所以i與p互質, 那么根據積性函數的性質 phi(i * p) == phi(i) * phi(p) 其中phi(p) == p-1
所以 phi(i * p) == phi(i) * (p-1).
再舉個例子:
假設i = 4, p = 3, i * p = 3 * 4 = 12
phi(12) = 12 * (1-1/2) * (1-1/3) = 4
phi(i) * (p-1) = phi(4) * (3-1) = 4 * (1-1/2) * 2 = 4 = phi(i * p)正確
include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 1e6+5;
bool flag[MAXN];///標記數組
int phi[MAXN];///歐拉函數值,i的歐拉函數值=phi[i]
int p[MAXN];///素因子的值
int cnt = 0;
void Get_phi()///篩法求歐拉函數
{
cnt = 0;
memset(flag, true, sizeof(flag));
phi[1] = 1;
for(int i=2; i<MAXN; i++)///線性篩法
{
if(flag[i])///素數
{
p[cnt++] = i;
phi[i] = i-1;///素數的歐拉函數值是素數 - 1
}
for(int j=0; j<cnt; j++)
{
if(i*p[j] > MAXN)
break;
flag[i*p[j]] = false;///素數的倍數,所以i*p[j]不是素數
if(i%p[j] == 0)///性質:i mod p == 0, 那么 phi(i * p) == p * phi(i)
{
phi[i*p[j]] = p[j] * phi[i];
break;
}
else
phi[i*p[j]] = (p[j]-1) * phi[i];///i mod p != 0, 那么 phi(i * p) == phi(i) * (p-1)
}
}
}
int main()
{
Get_phi();
int m;
while(cin>>m)///測試
{
cout<<phi[m]<<endl;
}
return 0;
}
費馬小定理
若 \(p\) 為素數, \(\gcd(a, p) = 1\) ,則 \(a^{p - 1} \equiv 1 \pmod{p}\) 。
另一個形式:對於任意整數 \(a\) ,有 \(a^p \equiv a \pmod{p}\) 。
證明:
由於p是質數,所以有\(φ(p) = p-1\),代入歐拉定理即可證明。推論:對於任意正整數a,有\(a^p ≡ a (mod p)\),因為a能被p整除時結論顯然成立。
擴展歐拉定理
\(a^{b} \equiv\left\{\begin{array}{ll} a^{b \bmod \varphi(p)}, & \operatorname{gcd}(a, p)=1 \\ a^{b}, & \operatorname{gcd}(a, p) \neq 1, b<\varphi(p) \quad(\bmod p) \\ a^{b \bmod \varphi(p)+\varphi(p)}, & \operatorname{gcd}(a, p) \neq 1, b \geq \varphi(p) \end{array}\right.\)
證明:
不會了,自己看下面吧:
鑒於篇幅過長所有本筆記分為上下兩部分
博客太菜了,可能會有錯誤(特別是筆誤),如發現請指明!
φ(n) ↩︎