1.1数论(持续更新ing)

本文介绍了数论的基本概念,包括整除、同余、最大公约数(GCD)和最小公倍数(LCM),以及数论在快速幂取模、线性同余方程和中国剩余定理中的应用。此外,还提及了素数的判定和相关定理,如威尔逊定理和费马定理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
    数论是纯粹数学的分支之一,主要研究整数的性质。整数可以是方程式的解(丢番图方程)。有些解析函数(像黎曼ζ函数)中包括了一些整数、质数的性质,透过这些函数也可以了解一些数论的问题。透过数论也可以建立实数和有理数之间的关系,并且用有理数来逼近实数(丢番图逼近)。按研究方法来看,数论大致可分为初等数论和高等数论。初等数论是用初等方法研究的数论,它的研究方法本质上说,就是利用整数环的整除性质,主要包括整除理论、同余理论、连分数理论。高等数论则包括了更为深刻的数学研究工具。它大致包括代数数论、解析数论、计算数论等等。//引自百度百科

建议搭配食用:我的小目录


1.1 整除

    设a是非零整数,b是整数。如果存在一个整数q,使得b=a*q,那么就说b可被a整除,记作a∣b,且称b是a的倍数,a是b的约数(因子)

    整除具有以下一些性质

  1. 如果a∣b且b∣c,那么a∣c
  2. a∣b且a∣c等价于对任意的整数x和y,有a∣(b×x+c×y)
  3. 设m≠0,那么a∣b等价于(m×a)∣(m×b)
  4. 设整数x和y满足下式:a×x+b×y=1,且a∣n,b∣n,那么(a×b)∣n
    证明略
  5. 若b=q×d+c,那么d∣b的充要条件是d∣c
    (因为一些傻狗原因,这个*不太好用)
    没有例题

1.2 同余

    若a,b为两个整数,且他们的差a-b能被某个自然数m所整除,则称a在%m意义下与b同余,记为a≡b(mod m)。它意味着,a-b=m*k(k为一个整数)

对于整数a,b,c和自然数m,n,对模m同余具有以下性质

  1. 自反性   a≡a(mod m)
  2. 对称性   若a≡b(mod m),则b≡a(mod m)
  3. 传递性   若a≡b(mod m),b≡c(mod m),则a≡c(mod m)
  4. 同加性    若a≡b(mod m),则a+c≡b+c(mod m)
  5. 同乘性   若a≡b(mod m),则a×c≡b×c(mod m);若a≡b(mod m),c≡d(mod m),则a×c≡b×d(mod m)
  6. 同幂性   若a≡b(mod m),则aⁿ≡bⁿ(mod m)
  7. 推论1:  a*b mod k = (a mod k) * (b mod k) mod k
  8. 推论2:  若a mod p = x,a mod q = x,p、q互质,则a mod p*q = x
  9. 同余不满足同除性

例题:洛谷P1226
   快速幂取模
输入b,p,k的值,求b^p mod k的值。其中b,p,k*k为长整型数。

输入输出格式
输入格式:
三个整数b,p,k.

输出格式:
输出“b^p mod k=s”

s为运算结果

输入输出样例
输入样例#1:
2 10 9
输出样例#1:
2^10 mod 9=7

#include <bits/stdc++.h>
using namespace std;
typedef long long ll
ll b,p,k;
ll ans;
ll pow_mod(ll t) {
     if  (t == 1) return b%k;
     ans = Mod(t/2);
     ans = (ans*ans)%k; 
     if  (t%2 == 1) ans = (ans*(b%k))%k;  
     return ans;
}
int main() {
    cin >> b >> p >> k;
    cout << b << '^' << p << " mod " << k << '= '<< pow_mod(p) << endl;
    return 0;
}

这个看一眼推论一就会了⑧


1.3 最大公约数(GCD)

	最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。
	求最大公约数有多种方法,常见的有质因数分解法、短除法、辗转相除法、更相减损法。与
	最大公约数相对应的概念是最小公倍数。

注:a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),a,b的最小公倍数记为[a,b]。(这个写法只有天朝能用哦,老外不认的哦)

1 辗转相除法
原理:GCD(x,y)=GCD(y,y-x)

int GCD(int x,int y) {
	return y == 0 ? x : GCD(y,x%y);
}

2 二进制算法
通过不断去除因子2来降低常数
若x=y,则GCD(x,y)=x,否则
(1) 若x,y均为偶数,则GCD(x,y)=2*GCD(x/2,y/2)
(2) 若x为偶数,y为奇数,则GCD(x,y) = GCD(x/2,y)
(3) 若x为奇数,y为偶数,则GCD(x,y) = GCD(x,y/2)
(4) 若x,y均为奇数,则GCD(x,y) = GCD(x-y,y)

inline int GCD(int x,int y) {
	int i,j;
	if (x == 0) return y;
	if (y == 0) return x;
	for (i = 0;0 == (x & 1);++i) x >>= 1;
	for (j = 0;0 == (y & 1);++j) y >>= 1;
	//去掉所有的2
	if (j<i) i = j;
	while (1) {
		if (x < y ) swap(x,y);
		if (0 == (x -= y)) return y << i;
		//辗转相减
		while (0 == (x&1)) x >>= 1;
		//去掉所有的2
	}
}

3.最小公倍数(LCM)
求解方法:
∀整数a,b,n

1. 逐步倍增
让n从1开始逐步加1并判断b∣a*n是否成立,成立时a*n即为a,b的最小公倍数
2. 定理:GCD(a,b)×LCM(a,b)=a×b

4 扩展欧几里得算法
已知a, b,求解一组x,y,使它们满足a×x+b×y =gcd(a, b)
证明:

假设 a>b,

(1) b=0 gcd(a,b) = a , ax = a , 则x=1,y=0;(这里我还是推荐不把gcd(a,0)理解成最大公约数,而是一个计算机求出来的值)

(2) 假设 ax1+by1=gcd(a,b) (方程一) bx2+(a%b)y2=gcd(b,a%b)(方程二);由欧几里得算法gcd(a,b) =gcd(b,a%b) 得到,

ax1+by1 = bx2+(a%b)y2,即ax1+by1=bx2+(a-a/bb)y2 ax1+by1=ay2+b(x2-a/by2)

在根据多项式恒等定理(把a,b看成变量),x1=y2; y1=x2-a/b*y2;

(表面上看,就是已知方程一的一组解,可以得到方程二的一组解,已知方程二的一组解,就可以得到方程一的一组解,但是实际情况是,不可能先知道方程一的解(x1,y1)。)上述思想是递归定义的,不断地利用gcd(a,b) =gcd(b,a%b),到b=0(y的系数为0)时,由(1)的解,根据解之间的关系,最终可以得到方程ax+by =gcd(a, b)的解。

代码如下

#include<bits/stdc++.h>
using namespace std;

int exgcd(int a,int b,int &x,int &y) {
     if (b == 0) {
        x = 1,y = 0;
        return a;
    }
    int gcd = exgcd(b,a%b,x,y);
    int x2 = x,y2 = y;
    x = y2;
    y = x2-(a/b)*y2;
    return gcd;
}

int main() {
	int x,y,a,b;
	cin>>a>>b;
	cout << exgcd(a,b,x,y) << endl;
	cout << x << " " << y << endl;
	return 0;
}

5 求解线性同余方程
定理1 : 对于方程a * x+b * y=c,该方程等价于a*x≡c(mod b),有整数解的充分必要条件是:c%GCD(a,b)=0
应用:对于方程a * x+b * y = c,我们可以先用扩展欧几里得算法求出一组解x0,y0,然后两边同时除以GCD(a,b),再乘以c。这样就得到了方程a * x0 * c / GCD(a,b) + b * y0 * c / GCD(a,b) = c,我们也就找到了方程的一组解。
定理2 : 若GCD(a,b) = 1,且x0,y0为a * x+b * y = c的一组解,则该方程的任一解可表示为x = x0+b * t,y = y0-a * t,且对于任一整数t,皆成立
应用2 : 根据定理2,可以求出方程的所有解。但在实际问题中,我们往往被要求去求最小整数解,也就是去求一个特解x,t=b/GCD(a,b),x=(x%t+t)%t,代码如下:

	int Ex_Eu(int a,int b,int &x,int &y) {
		if (b == 0) {
			x = 1,y = 0;
			return a;
		}
		int d = Ex_Eu(b,a%b,x,y);
		int temp = x,x =y,y = temp-a/b*y;
		return d;
	}

	//用扩展欧几里得算法解线性方程ax+by=c
	bool linearEquation(int a,int b,int c,int &x,int &y) {
		int d = Ex_Eu(a,b,x,y);
		if (c%d) return false;
		int k = c/d;
		x *= k;
		y *= k;
		return true;
	}

例题time
LuoguP1290欧几里得的游戏
欧几里德的两个后代Stan和Ollie正在玩一种数字游戏,这个游戏是他们的祖先欧几里德发明的。给定两个正整数M和N,从Stan开始,从其中较大的一个数,减去较小的数的正整数倍,当然,得到的数不能小于0。然后是Ollie,对刚才得到的数,和M,N中较小的那个数,再进行同样的操作……直到一个人得到了0,他就取得了胜利。下面是他们用(25,7)两个数游戏的过程:

Start:25 7

Stan:11 7

Ollie:4 7

Stan:4 3

Ollie:1 3

Stan:1 0

Stan赢得了游戏的胜利。

现在,假设他们完美地操作,谁会取得胜利呢?

输入输出格式
输入格式:
第一行为测试数据的组数C。下面有C行,每行为一组数据,包含两个正整数M, N。(M, N不超过长整型。)

输出格式:
对每组输入数据输出一行,如果Stan胜利,则输出“Stan wins”;否则输出“Ollie wins”

输入输出样例
输入样例#1:
2
25 7
24 15
输出样例#1:
Stan wins
Ollie wins

参考程序

#include <bits/stdc++.h>
using namespace std;
int main() {
  int c;
  cin >> c;
  for (int i = 0;i < c;i++) {
    int m,n;
    cin >> m >> n;
    if (m < n) {
      swap(m,n);
    }
    int f = 1;
    while (m/n == 1 && m%n) {
        int t = m%n;
        m = n;
        n = t;
        f = -f;
    }
    if (f == 1) {
      cout << "Stan ins\n";
    } else cout << "Ollie ins\n";
  }
  return 0;
}

1.4 逆元

方程ax≡1(mod p),的解称为a关于模p的逆,当gcd(a,p)==1(即a,p互质)时,方程有唯一解,否则无解。

对于一些题目会要求把结果MOD一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,

但对于(a/b)%MOD这个式子,是不可以写成(a%MOD/b%MOD)%MOD的,但是可以写为1/(a*b)其中b^-1表示b的逆元。

利用快速幂及扩展欧几里得算法求逆元的代码如下:

void exgcd(int a,int b,int c,int &x,int &y) {
		if (a = 0) {
			x = 0,y = c/b;
			return ;
		} else {
			int tx,ty;
			exgcd(b%a,a,tx,ty);
			x = ty-(b/a)*tx;
			y = tx;
			return ;
		}
}

线形算法:
过程略,其实是我忘了是啥了

A[i] = -(p*i)*A[p%i];

递归即可,时间复杂度Θ(log₂p)
LuoguP3811乘法逆元模板
题目背景
这是一道模板题

题目描述
给定n,p求1~n中所有整数在模p意义下的乘法逆元。

输入格式
一行n,p

输出格式
n行,第i行表示i在模p意义下的逆元。

代码如下:

#include<cstdio>
using namespace std;
long long p,c[3000005];
int main()
{
    long long n;
    scanf("%lld%lld",&n,&p);
    c[1]=1;
    printf("1\n");
    for(register int i=2; i<=n; i++)
    {
        c[i]=(p-p/i)*c[p%i]%p;
        printf("%lld\n",c[i]);
    }
    return 0;
}
//线形递推算法

1.5 孙子 中国剩余定理
注一下:(此部分中部分材料引自:巨佬的博客

问题:今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

强行翻译:∀x,模3等于2,模5等于三,模7等于二,求x。
//辅助材料姬


在这里插入图片描述


定理1:两个数相加,如果存在一个加数,不能被整数a整除,那么它们的和,就不能被整数a整除。

定理2:两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)。

具体步骤:
1、求出最小公倍数
lcm=357=105
2、求各个数所对应的基础数
(1)105÷3=35
35÷3=11…2 //基础数35
(2)105÷5=21
21÷5=4…1
定理2把1扩大3倍得到3,那么被除数也扩大3倍,得到213=63//基础数63
3、105÷7=15
15÷7=2…1
定理2把1扩大2倍得到2,那么被除数也扩大2倍,得到15
2=30//基础数30
把得到的基础数加和 (注意:基础数不一定就是正数)
35+63+30=128
4、减去最小公倍数lcm(在比最小公倍数大的情况下)
x=128-105=23
总结:已知m1,m2,m3是两两互质的正整数,求最小的正整数x,使它被m1,m2,m3除所得的余数分别是c1,c2,c3。孙子 (不是很雅观啊)中国剩余定理的思想便是先分别求出被其中数mi整除余1而被另外两个数整除的数Mi(i=1,2,3),则所求数之一的便是c1M1+c2M2+c3M3。由此我们可以得到n个两两互质数的情况。
例题time:
POJ1006:Biorhythms

生理周期
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 150107 Accepted: 48558
Description

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为10,下次出现三个高峰同天的时间是12,则输出2(注意这里不是3)。
Input

输入四个整数:p, e, i和d。 p, e, i分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。d 是给定的时间,可能小于p, e, 或 i。 所有给定时间是非负的并且小于365, 所求的时间小于21252。

当p = e = i = d = -1时,输入数据结束。
Output

从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。

采用以下格式:
Case 1: the next triple peak occurs in 1234 days.

注意:即使结果是1天,也使用复数形式“days”。
Sample Input

0 0 0 0
0 0 0 100
5 20 34 325
4 5 6 7
283 102 23 320
203 301 203 40
-1 -1 -1 -1
Sample Output

Case 1: the next triple peak occurs in 21252 days.
Case 2: the next triple peak occurs in 21152 days.
Case 3: the next triple peak occurs in 19575 days.
Case 4: the next triple peak occurs in 16994 days.
Case 5: the next triple peak occurs in 8910 days.
Case 6: the next triple peak occurs in 10789 days.

解法:

先来看一个故事
“韩信点兵”:传说西汉大将韩信,由于比较年轻,开始他的部下对他不很佩服。有一次阅兵时,韩信要求士兵分三路纵队,结果末尾多2人,改成五路纵队,结果末尾多3人,再改成七路纵队,结果又余下2人,后来下级军官向他报告共有士兵2395人,韩信立即笑笑说不对(因2395除以3余数是1,不是2),由于已经知道士兵总人数在2300~2400之间,所以韩信根据23,128,233,------,每相邻两数的间隔是105(3、5、7的最小公倍数),便立即说出实际人数应是2333人(因2333=128+20χ105+105,它除以3余2,除以5余3,除以7余2)。这样使下级军官十分敬佩,这就是韩信点兵的故事。
韩信点兵问题简化:已知 n%3=2, n%5=3, n%7=2, 求n。 再看我们这道题,读入p,e,i,d 4个整数
已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i ,求n 。
两道题是一样的。但是韩信当时是怎样计算出结果的? 他用的就是“中国剩余定理” “韩”问题计算如下: 因为n%3=2, n%5=3,
n%7=2 且 3,5,7互质 (互质可以直接得到这三个数的最小公倍数) 令x= n%3=2 , y= n%5=3 ,z= n%7=2
使5×7×a被3除余1,有35×2=70,即a=2;
使3×7×b被5除余1,用21×1=21,即b=1;
使3×5×c被7除余1,用15×1=15,即c=1。 那么n =(70×x+21×y+15×z)%lcm(3,5,7) = 23 这是n的最小解 而韩信已知士兵人数在2300~2400之间,所以只需要n+i×lcm(3,5,7)就得到了2333,此时i=22
同样,这道题的解法就是: 已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i
使33×28×a被23除余1,用33×28×8=5544;
使23×33×b被28除余1,用23×33×19=14421;
使23×28×c被33除余1,用23×28×2=1288。
因此有(5544×p+14421×e+1288×i)% lcm(23,28,33) =n+d 又23、28、33互质,即lcm(23,28,33)= 21252;
所以有n=(5544×p+14421×e+1288×i-d)%21252 本题所求的是最小整数解,避免n为负,因此最后结果为n= [n+21252]% 21252 那么最终求解n的表达式就是:
n=(5544p+14421e+1288*i-d+21252)%21252;

代码如下:

#include<iostream>
using namespace std;
 
int main(void)
{
	int p,e,i,d;
	int time=1;
	while(cin>>p>>e>>i>>d)
	{
		if(p==-1 && e==-1 && i==-1 && d==-1)
			break;
 
		int lcm=21252;  // lcm(23,28,33)
		int n=(5544*p+14421*e+1288*i-d+21252)%21252;
		if(n==0)
			n=21252;
		cout<<"Case "<<time++<<": the next triple peak occurs in "<<n<<" days."<<endl;
	}
	return 0;
}

还有这个,中国剩余定理模板

/*long long gcd(LL a,LL b)
{
    return b==0?a:gcd(b,a%b);
}*/
#include<cstdio>
#define ll long long
//扩展欧几里得算法 
void gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
    if(b==0){
        d=a;
        x=1,y=0;
    }
    else{//else不能省略 
        gcd(b,a%b,d,y,x);
        y-=(a/b)*x;
    }
}
//中国剩余定理 
ll China(int n,ll *m,ll *a)
{
    ll M=1,d,y,x=0;
    for(int i=0;i<n;i++) M*=m[i];
    for(int i=0;i<n;i++){
        ll w=M/m[i];
        gcd(m[i],w,d,d,y);
        x=(x+y*w*a[i])%M;
    }
    return (x+M)%M;
}
ll m[15],a[15];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lld%lld",&m[i],&a[i]);
    printf("%lld",China(n,m,a));
}

1.6 卡特兰数
卡特兰数是一个很常用的东西,嗯,具体介绍百度吧
她的前几项为:1、2、5、14、42、132、429、1430、4862、16796、58786、208012、742900、…
求卡特兰数的第n项,可以用以下几个公式:
1.递归1
     n-1
f(n) = ∑f(i)f(n-i-1)
      i
2.递归2
f(n) = (f(n-1) * (4
n-2))/n+1
3.组合1
f(n)=C(n,2n)/n+1
4.组合2
f(n)=C(n,2n)-C(n-1,2n)


1.7 素数

素数又称质数,是指一个大于一的正整数,且除了1和它本身之外没有任何因子,反之就是合数,1既不是素数也不是合数。
小于x的素数个数函数值对照表

1 素数的判定
穷举法略
筛法

void prime() {
	memset(p,1,sizeof(p));
	p[0] = false,p[1]=false;
	int N = 31700;
	for (int i  = 2;i < N;i++) {
		if (p[i) {
			p[++cnt] = i;
			for (int k = i*i;k < N;k += i) {
				p[k] = false;
			} 
		}
	}
	return ;
}

线性筛

#include <bits/stdc++.h>
using namespace std;
const long N = 200000;
long prime[N] = {0},num_prime = 0;
int isNotPrime[N] = {1,1};
int main() {
  for (long i = 2;i < N;i++) {
    if (!isNotPrime[i]) prime[num_prime++] = i;
    for (long j = 0;j < num_prime && i*prime[j] < N;j++) {
      isNotPrime[i*prime[j]] = 1;
      if (!(i%prime[j])) break;
    }
  }
  return 0;
}

2 素数的相关定理
 2.1 唯一分解定理

若整数a≥2,那么整数a一定可以表示为若干个素数的乘积

  ·存在性证明:

假设存在大于1的自然数不能写成质数的乘积,把最小的那个称为n。

自然数可以根据其可除性(是否能表示成两个不是自身的自然数的乘积)分成3类:质数、合数和1。首先,按照定义,n 大于1。其次,n
不是质数,因为质\数p可以写成质数乘积:p=p,这与假设不相符合。因此n只能是合数,但每个合数都可以分解成两个严格小于自身而大于1的自然数的积。设,其中a
和b 都是介于1和n 之间的自然数,因此,按照n 的定义,a 和b 都都可以写成质数的乘积。从而
也可以写成质数的乘积。由此产生矛盾。因此大于1的自然数必可写成质数的乘积。

  ·唯一性证明:

命题描述:若质数p|ab,则不p|a就p|b
设c=ab;p为质数,若p量尽a,得证;若p量不尽a(即p不是a的因子),证明p量尽b即得证。

根据(7-29),因为p为质数,且p量不尽a,所以p、a互质。

用反证法证明如下:设p、a不互质,将有公因子m,因为m是a的因子,p不是a的因子,所以m不等于p,而m是p的因子,这是不可能的。

根据(7-定义15),c/p=t,得到c=pt,又c=ab,根据(7-19)得到p/a=b/t

根据(7-21),因为p、a互质,p、a是具有相同比的数对中最小的一对。

再根据(7-20),大小两数a、p分别量尽具有同比的大小两数t、b,所得的次数相同,即前项量尽前项、后项量尽后项,所以p量尽b,即得证。

最后一条定理(7-20)证明的难度较大,特证明如下:

命题:设a、b是与a/b有相同比的数对中最小的一对数,如果a/b=c/d,则a=(1/n)*c;b=(1/n)*d。

证明:因为a<c,所以a=(1/n)*c或者a=(m/n)*c,[1<m<n]

根据(7-13和定义20),若a=(m/n)*c,那么也有b=(m/n)*d

这样a、b均有(1/m)的部分

根据(7-12),(1/m)*a : (1/m)*b = a : b

但是(1/m)*a 、 (1/m)*b 分别小于 a 、b,这与假设矛盾,所以仅有a=(1/n)*c

根据(7-13和定义20),有a=(1/n)*c,b=(1/n)*d,故得证。

 2.2 威尔逊定理
  ·描述:

p 为质数–>(p-1)!\equiv -1(\mod p)

 2.3 费马定理
3 Miller-Rabin 素性测试
4 欧拉定理
5 Pollard Rho破腊肉 算法求大数因子

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值