紫书上第十章的例题刷了有一半了,相关知识也学了学,但总感觉忘得很快,水一水博客,把自己的笔记总结一下,顺便清理一下收藏夹,哈哈哈。
10.1 数论初步
一、欧几里得算法(辗转相除法)
ll gcd(ll a, ll b) //while循环
{
ll r=a%b;
while(r)
{
a=b;
b=r;
r=a%b;
}
return b;
}
ll gcd(ll a, ll b) //递归写法
{
return b?gcd(b,a%b):a;
}
int lcm(int a, int b)
{
return a/gcd(a, b)*b;
}
二、筛法
int prime[maxn];
bool vis[maxn]={false};//用vis[i]表示i已经被删除
int cnt=0;
void Era_prime(int n)
{
for(int i=2; i<=n; i++)
{
if(!vis[i]) prime[cnt++]=i;//是素数
for(int j=i; j<=n; j+=i)
vis[j]=true;
}
}
int prime[maxn], cnt=0;//prime记录素数,cnt记录素数个数
bool vis[maxn]={false};//vis记录当前数是否被筛去
void euler_prime(int n)
{
for(int i=2; i<=n; i++)
{
if(vis[i]==false) prime[cnt++]=i;//未被筛过,是素数
for(int j=0; j<cnt; j++)
{
if(i*prime[j]>n) break;//当要标记的合数超出范围时跳出
vis[i*prime[j]]=true;//将已经记录的素数的倍数进行标记
if(i%prime[j]==0) break;//关键步骤,保证每个合数被它最小的质因数筛去
}
}
}
三、扩展欧几里得算法
抄一抄度娘的定义:众所周知,有两个数a, b,对它们进行辗转相除法,可得它们的最大公约数,扩展欧几里得算法还能找到一对整数(x, y),即ax+by=gcd(a, b)的整数解,(x, y可能是负数或者0)
简言之,扩展欧几里得算法的作用:得到ax+by=gcd(a,b)的所有整数解
代码解释:前面提到,辗转相除法的关键在于如下恒等式:gcd(a, b)=gcd(b, a%b),
不断向下取余,当b==0时即为边界,gcd(a, 0) = a = a * 1 + 0 * 0—>(x=1, y=0),这样我们就知道了最底层的一组解,再利用推导出来的公式(这里不懂得嘤)求出上面的解
(注意在递归算法中,永远都是先得到下面一个状态的值)
附上一些链接,希望以后能完全理解
b站证明扩展欧几里得视频
扩展欧几里得算法详解
0830-扩展欧几里得算法+例题
//模板-求出ax+by=gcd(a, b)的一组解
void gcd(int a, int b, int& d, int& x, int& y)
{
if(!b)
{
d=a; //到达边界时d开始往上层返回
x=1;
y=0;
}
else
{
gcd(b, a%b, d, y, x); //d是a和b的最大公因数
y-=x*(a/b);
}
}
//还见到了另一种写法,帮助理解
int gcd(int a, int b, int& x, int& y)
{
int d=a;
if(!b) { x=1; y=0;}
else { d=gcd(b, a%b, y, x); y-=a/b*x; }
return d;
}
//例题(来源:博主:境外之主)
#include
typedef long long LL;
void extend_Eulid(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = 1, y = 0;}
else{
extend_Eulid(b, a % b, y, x, d);
y -= x * (a / b);
}
}
int main(){
LL a, b, d, x, y;
while(~scanf("%lld%lld", &a, &b)){
extend_Eulid(a, b, x, y, d);
printf("%lld*a + %lld*b = %lld\n", x, y, d);
}
}
得到一组解后,如何得到其他解呢?
设a, b, c为任意整数,g=gcd(a, b),方程ax+by=g的一组解是(x0, y0),则当c是g的倍数时ax+by=c的一组解是(x0c/g, y0c/g);当c不是g的倍数时无整数解
模板题:例10-2 UVA12169
四、同余与模运算
scanf("%s%d", n, &m);
int len=strlen(n);
int ans=0;
for(int i=0; i<len; i++)
ans=(int)(((long long)ans*10+n[i]-'0')%m);
printf("%d", ans);
//递归写法
unsigned long long a, b;
int pow_mod(unsigned long long a, unsigned long long b, int c)
{
if(b==0) return 1;
if(b&1) return a*pow_mod(a, b-1, c)%c;
else
{
unsigned long long s=pow_mod(a, b/2, c)%c;
return s*s%c;
}
}
五、补充知识