假期培训日记(数论)

目录

一、基础知识

1、同余定理

2、a|b

二、素数筛

1、埃式素数筛

2、欧拉素数筛

三、快速幂

1、分解质因数

2、快速幂(一般都是对m取余)

四、欧几里得算法

1、辗转相除法

2、扩展欧几里得

五、中国剩余定理

1、孙子算经

2、中国剩余定理


一、基础知识

1、同余定理

定义:

给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系

性质:                  

(注意不再是充要条件了)

(下面的特例)

若gcd(c,n)!=1,a≡b(mod n/gcd(c,n)  (不会证明)

2、a|b

意思是b可以被a整除


二、素数筛

1、埃式素数筛

先来个基础的过程:

1.1~n列出来

2.去掉不是特殊的1

3.从小到大,枚举每一个没有删掉的数字i

4.i2倍,3倍,4倍,,删掉

5.剩下的没被删掉的都是素数

const int n=100001;
int book[n];//标记是否删除
memset(book,0,sizeof(book));//初始化,为0代表没删除,为1代表删除
book[1]=1;//1是特例
for(int i=2;i<n;i++)
    if(!book[i])
    {
        for(j=2;j*i<n;j++)
            book[j*i]=1;
    }//book为0就是素数了

但是这种方法有些数被删除了两次,比如6既被2删除又被3删除,没有必要删那么多次。有一个小优化是把j初始设为i*i,因为i*(i-1),i*(i-2)这些已经被i-1和i-2删除了。但并不能解决所有问题,比如60=2*30=3*20,还是会重复,我们希望每个数只被删一次。

2、欧拉素数筛

#include <stdio.h>
int book[100001],prime[100001];//book说明是否被删,prime保存素数
int main()
{
	int n,i,j,top=0;
	scanf("%d",&n);
	for(i=2;i<=n;i++)
	{
		if(!book[i])
			prime[top++]=i;//没有被删去,那就当素数留下来。
		for(j=0;j<top;j++)//注意,无论是否被删除,都进行循环
		{
			book[prime[j]*i]=1;
			if(!(i%prime[j]))//此时素数是i的因子就停下来
				break;
		}
	}
	for(i=0;i<top;i++)
		printf("%d ",prime[i]);
}

欧拉的主干就是每个数只被自己最小的因子删去。对于这种筛法,最好不要把book[i]理解成删除,还有用,最好理解成留作最大公因子。对于任意合数n,都等于最小公因子与最大公因子之积。对于任意两个不同的数,最大公因子与最小公因子一定不同时相同。每一个删除的数都由它的最小公因子与最大公因子相乘获得,就能保证每次删除的数不同。

当数为i时,把i当作之后某个数的最大公因子。若没被标记,证明是素数,先加到素数组里。此时,素数组肯定都小于等于它,素数组里的都当作最小公因子。相乘开始删,注意到i可以整除此时的素数prime[j]时停止。此时i=x*prime[j],如果往后乘,i*prime[j+1]=x*prime[j+1]*prime[j],对于x*prime[j+1]*prime[j]来说,最大公因子是x*prime[j+1]比i大,最小公因子是primr[j]比prime[j+1]小,所以不能用i*prime[j+1]获得。

这样由于每一次删除数时最大或最小公因子总有一个不同,一定删除的不同。


三、快速幂

1、分解质因数

算术基本定理:任何一个大于1的自然数N,如果N不是素数,那么N可以唯一地表示为                                                                  

 

方法一:从2n反复除以能除的数 O(n)

t=0;//因子的个数
for(int i=2;i<n;i++)
{
    if(!n%i)
    {
        temp[++t]=i;//第t个因子的值
        count[t]=n/i;//次方
    }
}//n是质数在下面的样程里写了

方法二:素数筛预处理最小素因子,循环除以最小素因子 O(n)+O(logn)

count=t=0;
for(i=0;i<pn&&prime[i]<=n;i++)//pn是小于n的质数的个数
{
    if(!n%prime[i])
    {
        temp[++t]=prime[i];
        count[t]=n/prime[i];
    }
}
if(temp[t])//n是质数
{
    temp[t++]=1;
    temp[t]=n;
    count[t]=1;
}

2、快速幂(一般都是对m取余)

其实快速幂就是a*a得到a^{2},那么之后就可以a^{2}*a^{2}=a^{4}......不用a*a*a*a了

所以就从a^{n}开始向下分解,n为偶数a^{n}mod m=(a^{\frac{n}{2}}mod m)^{2}mod m;n为奇数a^{n}mod m=((a^{\frac{n}{2}}mod m)^{2}mod m)*(a mod m)mod m。

一直向下分解到a的0次方返回1。

递推法

long long quick(int n)
{
    if(n==0)
        return 1;
    int half=quick(n/2);
    if(n%2)
        return (half*half%m)*(n%m);
    else
        return half*half%m;
}

非递归版

long long quick()
{
    res=1;
    while(n)
    {
        if(n%2)
            res=res*a%m;
        a=a*a%m;
        n/=2;
    }
    return res;
}

遗留问题

1、矩阵快速幂

2、FFT优化

3、高精度乘法优化


四、欧几里得算法

#include <algorithm>里面有__gcd函数(是双下划线,而且有复杂度)

1、辗转相除法

        gcd(1350,211)为例

 

证明:

充分性:

a可以表示成a = kb + r(a,b,k,r皆为正整数,且r<b),则r = a mod b

假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。

而r = a - kb,两边同时除以d,r/d=a/d-kb/d=m,由等式右边可知m为整数,因此d|r

因此d也是b,a mod b的公约数

必要性:

 假设d是b,a mod b的公约数, 则d|b,d|(a-k*b),k是一个整数。

进而d|a.因此d也是a,b的公约数

因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。

long long gcd(a,b)
{
    if(!b)
        return a;
    return gcd(b,b%a);
}

最大公约数有结合律

代码实现一串数字的最大公约数

res=gcd(a[0],a[1]);
for(int i=2;i<n;i++)
    res=gcd(res,a[i]);

最小公倍数

lcm=(a/gcd(a,b))*b;//先除后乘,防止溢出

证明:

a=n*gcd(a,b);

b=m*gcd(a,b);

n,m互质,所以最小公倍数为a*b*gcd(a,b);

2、扩展欧几里得

对于不完全为 0 的非负整数a、b,必然存在整数对x、y,使得gcd(a,b)=ax+by。

求解 x,y的方法的理解

设 a>b。

1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;

2,a>b>0 时,设

                                                                                        ax1+ by1= gcd(a,b);

                                                                           bx2+ (a mod b)y2= gcd(b,a mod b);

根据朴素的欧几里得原理有 gcd(a,b) = gcd(b,a mod b);

则:                                                                                   ax1+ by1= bx2+ (a mod b)y2;

即:                                                             ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;

                                                                          ([a/b]代表取小于a/b的最大整数)

也就是                                                                          ax1+ by1 == ay2+ b(x2- [a / b] *y2);

                                                                                     x1=y2;

                                                                                     y1=x2- [a / b] *y2;

这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.

上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。

int x,y;
void exgcd(int a,int b,int x,int y)//不用管x,y的值,后面会赋值往前推,前面的x,y值无意义
{
    if(!b)
    {
        x=1;
        y=0;
        return ;
    }
    exgcd(b,a%b,x,y);//这一步执行完x,y就被附上有意义的值 b*x+a%b*y=gcd(b,a%b)
    int t=x;
    x=y;
    y=t-a/b*y;//完成x,y的更新 a*x+b*y=gcd(a,b)
}

应用

1、求解线性方程                 

 

int solve()
{
    if(b%gcd(a,n))
        return -1;//如果b不是gcd(a,n)的倍数,无整数解
    int x,y;
    exgcd(a,n,x,y);
    return x*b/gcd(a,n);
}

 

2、求逆元 

等于1的特殊情况。


五、中国剩余定理

1、孙子算经

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?即,一个整数除以三余二,除以五余三,除以七余二,求这个整数

凡三三数之剩一,则置七十;五五数之剩一,则置二十一;七七数之剩一,则置十五.一百六以上,以一百五减之,即得

2、中国剩余定理

以例题为例。

1、N是3,5,7的公倍数105。

2、N1=35,N2=21,N3=15。

3、x=a1*N1+a2*N2+a3*N3时(此时a1,a2,a3还是未知的),N2、N3都是3的倍数,x mod 3=a1*N1 mod 3=2,同理可得x mod 5=a2*N2 mod 5=3,x mod 7=a3*N3 mod 7=2。

4、更直观一点,代入35,21,15

35*a1\equiv2(mod 3)

21*a2\equiv3(mod 5)

15*a3\equiv2(mod 7)

用扩展欧几里得求得a1,a2,a3

求出x=a1*N1+a2*N2+a3*N3

5、但此时x可能很大,不停减去公倍数105,缩小范围,也保证缩小后的数一定成立,就是x%=105了。

多次减去105不影响数满足初始条件,(x-105)mod n=x mod n +105 mod n=x mod n(这个等式不太严谨,理解就行)

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值