# 数论基础总结
数论基础总结
数论基础
整除
定义 若整数$b$除以非零整数$a$,商为整数,且余数为零, 就说$b$能被$a$整除(或说$a$能整除$b$)。
同余
若整数$a$和整数$b$除以正整数$m$的余数相等,则称$a,b$模$m$同余,记为$a \equiv b \pmod{m}$
整数唯一分解定理
任何一个大于1的自然数$N$,如果$N$不为素数,那么$N$可以唯一分解成有限个素数的乘积:
$N=P^{a_1}_1P^{a_2}_2......P^{a_n}_n$
$P_1 < P_2 <......< P_n$均为质数,指数$a_i$为正整数。这样的分解称为$N$的标准分解式。
素数
素数概念:一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做素数;否则称为合数(相应的,1既不是素数也不是合数)
求素数的方法:
暴力求解
#include<bits/stdc++.h>
using namespace std;
bool Prime(int n){
for(int i=2;i<n;i++){
if(n%i==0)return false;
}
return true;
}
int main(){
int n;
cin>>n;
if(Prime(n)){
cout<<"Yes";
}else{
cout<<"No";
}
}
很明显,时间复杂度$O(n)$,$n$一大就炸。
对暴力做一点点优化
由于如果有一个数$x$满足$x=a \times b$,那么$x$必然满足$x = b \times a$。
那这有什么用呢?借助这一性质可以对暴力做一点点优化:
#include<bits/stdc++.h>
using namespace std;
bool Prime(int n){
for(int i=2;i*i<=n;i++){
if(n%i==0)return false;
}
return true;
}
int main(){
int n;
cin>>n;
if(Prime(n)){
cout<<"Yes";
}else{
cout<<"No";
}
}
就这样,时间复杂度$O(\sqrt n)$。
埃氏筛
但是如果检测多个数,还是太慢了,优化,必须大大滴优化。
借着质数的倍数是合数的理念,终于可以得到:
int p[N], v[N];
void Prime(int n) {
int i, j;
for (i = 2; i <= n; i++) {
if (v[i]) continue;
p[++p[0]] = i;
for (j = 2 * i; j <= n; j += i) v[j] = 1;
}
}
时间复杂度$O(n\log \log n)$。
欧式筛(线性筛)
不过埃氏筛有很多是算重复了的,不行,必须把这个$\log \log n $给摘掉。
于是乎在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,就可以达到不重复的目的。
int p[N], v[N];
void Prime(int n) {
int i, j;
for (i = 2; i <= n; i++) {
if (!v[i]) p[++p[0]] = i;
for (j = 1; j <= p[0]; j++) {
if (i * p[j] > n) break;
v[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}
$O(n)$使我疯狂!!!
GCD 和 LCM
约数与倍数
如果整数$a$能被整数$b$整除,$a$就叫做$b$的倍数,$b$就叫$a$的约数。
公约数与公倍数
几个整数中公有的约数,叫做这几个整数的公约数。
几个整数中公有的倍数,叫做这几个整数的公倍数。
最大公约数与最小公倍数
几个整数的公约数中,最大的叫做这几个数的最大公约数。
几个整数的公倍数中,最小的叫做这几个数的最小公倍数。
辗转相除法求GCD
int gcd(int a,int b)
{
if(a%b==0)
return b;
else return (gcd(b,a%b));
}
拓展欧几里得:
解$ax+by=\gcd(a,b)$这个方程。
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;
}
LCM
$LCM(a,b)=(a/\gcd(a,b))\times b$
欧拉函数
欧拉函数表示的是小于等于$n$和$n$互质的数的个数。
显然,当$n$为素数,$\varphi(n)=n-1$
互质:多个数最大公因数只有1。
欧拉函数求法:
$\varphi(n)=n \times \frac{p_1-1}{p_1} \times \frac{p_2-1}{p_2}\times ...... \times \frac{p_r-1}{p_r}$
各个数值同整数唯一分解定理。
欧拉函数模板
int euler(int n) {
int ans = n;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
void phi(int n, int phi) {
for (int i = 2; i <= n; i++) phi[i] = 0;
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!phi[i]) {
for (int j = i; j <= n; j += i) {
if (!phi[j]) phi[j] = j;
phi[j] = phi[j] / i * (i - 1);
}
}
}
}
欧拉定理和业余费马小定理
欧拉定理
与欧拉函数密切相关。
若$\gcd (a,m)=1$,则$a^{\varphi(m)}\equiv 1(\mod m)$
欧拉的计算
int euler(int x) {
int i,ans=x,a=x;
for(i=2; i*i<=a; i++) {
if(a%i==0) {
ans=ans-ans/i;
while(a%i==0) {
a=a/i;
}
}
}
if(a>1) ans=ans-ans/a;
return ans;
}
筛法
void Selcet_Euler(int n) {
int i,j;
for(i=1; i<=n; i++) phi[i]=i;
for(i=2; i<=nli++) {
if(phi[i]==i) {
for(j=i; j<=n; j+=i) {
phi[j]=phi[j]/i*(i-1);
}
}
}
}
利用欧拉函数的性质($p$为素数):
- $phi(p)=p-1$
2.如果 $i \mod p = 0$,那么$phi (i \times p)=p \times phi(i)$
3.若$i \mod p \not = 0$,那么$phi(i\times p)=phi(i)\times (p-1)$
由此,可以提出欧拉函数的线性筛法:
void Phi() {
int i,j,tot=0;
for(i=2; i<=N; i++) {
if(Mark[i]==false) {
Prime[++tot]=i;
phi[i]=i-1;
}
for( j=1; j<=tot && i*Prime[ j]<=N; j++) {
Mark[i*Prime[ j]]=true;
if(i%Prime[ j]==0) {
phi[i*prime[ j]]=phi[i]*Prime[ j];
break;
}
else phi[i*Prime[ j]]=phi[i]*(Prime[ j]-1);
}
}
}
费马小定理
这个定理不费马,但费人
费马定理:设$p$是素数,$a$是整数,且$(a,p)=1$,则$a^{p-1} \equiv 1(\mod p)$
证明:考虑$1,2,3······(p-1)$这$(p-1)$个数字,同时乘上$a$,得到了$a,2a,3a······(p-1)a$。
$\because a \not = b(\mod p),(c,p)=1$
$\therefore ac \not = bc(\mod p)$
$\therefore 1 \times 2 \times 3......(p-1) \equiv a\times 2a\times 3a......(p-1)a(\mod p)$
$\therefore \gcd (1 \times 2 \times 3\times ...... \times (p-1),p)=1$
$\therefore a^{p-1} \equiv 1(\mod p)$
Miller-Rabin
前置:
1.费马小定理
2.二次探测定理
二次探测定理:如果$p$是一个素数,且$0 < x < p$,则方程$x^2 \equiv 1(mod p)$的解为$x=1,p-1$。
证明:显然$x^2-1 \equiv 0(\mod p)$
$\therefore (x+1)(x-1) \equiv 0(\mod p)$
$\because p$为素数
$\therefore x=1$或者$x=p-1$
模板:
bool MR(__int128 n) {
__int128 a,x,y,d,r=0;
if(n==2) return 1;
if(n<3 || n%2==0) return 0;
d=n-1;
while(d%2==0) d/=2,r++;
for(__int128 i=1;i<=30;i++) {
a=rand()%(n-2)+2,x=qmi(a,d,n);
for(__int128 i=1;i<=r;i++) {
y=x*x%n;
if(y==1 && x!=1 && x!=n-1) return 0;
x=y;
}
if(x!=1) return false;
}
return 1;
}
乘法逆元
逆元:
给定正整数$a,p$,如果有$ax \equiv 1(\mod p)$,且$a$与$p$互质,则称$x$的最小正整数解为$a \mod p$的逆元。
递推求逆元:
inv[1] = 1;
for (int i = 2; i <= n; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
拓展欧几里得:
辗转相除法的基础上求得的用于计算满足$\gcd(a,b)=ax+by$的整系数x和y。
使用ex-欧几里得来求逆元:
void exgcd(int a,int b,int &x,int &y) {
if(b == 0) {
x = 1; y = 0;
return ;
}
exgcd(b,a%b,x,y);
int t = x;
x = y;
y = t - a / b * y;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战