目录
1. 唯一分解定理
2.欧几里德算法(求最大公约数)
3.求最小公倍数
4.埃氏筛
5.拓展欧几里德算法
(1)证明一下线性方程组的正数的最小值是多少,
(2)如何通过裴蜀定理退出拓展欧几里得算法(贝祖定理)
6.同余与模算术
(1)取模运算操作
加法取模运算
减法取模运算
乘法取模运算
(2)特殊的取模操作
大整数取模
幂取模
(3)同余式,乘法逆元,费马小定理
今天也是小小的开始学习数论方面的知识了,首先数论的入门章节必然是欧几里德算法和唯一分解定理
定义:任何一个数都可以变成若干个质数相乘的形式
X=P1^a1*p2^a2*……*pk^ak;
也就是大家喜闻乐见的分解质因数
欧几里德算法,又称辗转相除法,是由递推式gcd(a,b)=gcd(b,a mod b)和边界gcd(a,0)=a得来
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
当然了,要是就最大公约数,《九章算术》中也提到了一种算法,名为更相减损术,也可以用于求最大公约数,每次都是大的数减去小的数,直到两数相同,就是最大公约数
int gcd(int a,int b)
{
while(a != b)
{
if(a>b) a -= b;
else b -=a;
}
return a;
}
我们由唯一分解定理可知
假设e为a的指数,f为b的指数
gcd(a,b)=p1^min(e1,f1) * p2^min(e2,f2) *…… *pk^min(ek,fk)
lcm(a,b)=p1^max(e1,f1) * p2^max(e2,f2) *…… *pk^max(ek,fk)
因此我们可以推出公式 gcd(a,b)*lcm(a,b)==a*b
那么我们的最小公倍数函数就为
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
不是哥们,这个你都信啊,有没有考虑过数据越界的问题,万一a*b数据越界怎么办?
所以上面那个是错的
实际上应该是,先除后乘,这样可以在一定程度上避免数据越界问题
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
用来求素数表的方法,这个是在欧拉的基础上改良的方法
int vis[200005];//用于判断哪些数是素数,标记数组,0为素数,1为合数
for(int i=2;i*i<=m;i++)
{
if(vis[i]==0)
for(int j=i*i;j<=n;j+=i)
{
vis[j]=1;
}
}
同时也有一个素数定理N(x)约等于·x / lnx
N(x)为不超过x的素数的个数
再说拓展欧几里德算法之前,首先要去了解一个定理:裴蜀定理
设有线性方程组 ax+by=c;,那么gcd(a,b)|c (a与b的最大公约数是c的因子),只有这样方程才有整数解
我们假设ax+by=d; d为gcd(a,b)
s为ax+by的最小正值,设r=a mod s
证明:
a/s=q 余下r
r=a-qs
因为s=ax+by
r=a(1-qx)+b(-qy)
因为a,b,x,y都为整数
所以r也为整数,且为a,b的线性组合
又因为0<=r
所以r的值只能为0
因此s|a(s为a的因子),同理可证s|b(s也为b的这因子)
所以s为a,b的公约数,并且d>=s(因为d为最大公约数)
又因为d|a,d|b
所以d|(ax+by)
所以d|s且s>0
所以d<=s
所以d==s;
假设有方程a*x1+b*y1=gcd(a,b)且有b*x2+(a%b)*y2=gcd(b,a%b);
由欧几里得算法可知gcd(a,b)==gcd(b,a%b)
所以 a*x1+b*y1==b*x2+(a%b)*y2
对于取模来说有一个公式 a%b=a-a/b*b;
将上面这个公式代入等式并且分配之后得到结果:
a*x1+b*y1=a*y2+b*(x2-a/b*y2 )
所以可以说明
x1=y2
y1=x2-a/b*y2
即:
x2=y1+a/b+y2;
y2=x1;
写出代码:
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;//记录的x2 ,y就是y2
x=y;
y=t-a/b*y;
return d;
}
用途:
欧几里德算法是用来计算两个整数的最大公约数的一种方法。拓展欧几里德算法是在计算最大公约数的同时,还能找到一组整数解,使得这组解满足贝祖等式。
拓展欧几里德算法的应用有以下几个方面:
线性同余方程的解:利用拓展欧几里德算法可以求解形如ax ≡ b(mod m)的线性同余方程的整数解。这在密码学中有很多应用,比如RSA加密算法中,就用到了线性同余方程来实现加密和解密。
模逆元的计算:在计算机中,取模运算的逆元是非常重要的,可以使得一些运算更高效,比如在快速幂运算等中。拓展欧几里德算法可以用来计算模逆元。
应用于数论问题:拓展欧几里德算法还可以应用于解决一些数论问题,比如求解模线性方程组,计算最小公倍数等。
(a+b)%mod=(a%mod+b%mod)%mod;
(a-b)%mod=(a%mod-b%mod+mod)%mod
(a*b)%mod=(a%mod*b%mod)%mod
那么就有人说了,这个是不是少了一个,是不是少了一个除法取模运算呢?答案是确实少了一个,但是那个涉及到乘法逆元了,我们要循序渐进
比如说这么一道题:输入正整数n和m,然后输出n mod m的值,n<=10^100,m<=10^9
思路:我们可以把大整数写成自左向右的形式:比如说1234就可以写成((1*10+2)*10+3)*10+4
然后再每步都取模
#include
using namespace std;
#define int long long
char s[105];
int mod;
signed main()
{
cin>>s;
cin>>mod;
ans=0;
for(int i=0;i<=s.size();i++)
{
ans=(ans*10%mod+(s[i]-'0')%mod)%mod;
}
cout<
输入正整数a,n,m,输出a^n mod m的值,a,n,m<=10^9
思路:最简单的模拟很好写
int mod(int a,int n,int m)
{
int ans=1;
for(int i=0;i
但是我们会发现这种写法的时间复杂度( O(logN) )实在是太高了,在实际生活中你愿意一个一个的搞吗?
当然是不会的,我们要想办法减小上面这段代码的时间复杂度 ,我们可以用递归的方式,每次让数据的规模减小一半
int mod(int a,int n,int m)
{
if(n==0)
return 1;
int x=mod(a,n/2,m);
ans=(x%mod*x%mod)%mod;
if(n%2==1)
ans=ans*a%mod;
return ans;
}
这样的时间复杂度仅为O( logN )将会大大减少代码的时间复杂度
http://t.csdnimg.cn/PrZrs
之前的文章有写过,直接看我之前文章就行