数论概念总结

关于数论

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次幂,

img

因为除了p的倍数外,其他数都跟n互质。

3、欧拉函数是积性函数——若m,n互质

img

4、当n为奇质数时,

img

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互质,则:

img

其实,当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];
	}
}
posted @ 2020-08-02 11:47  liuchanglc  阅读(957)  评论(2编辑  收藏  举报