紫书 第10章 数学概念与方法

紫书上第十章的例题刷了有一半了,相关知识也学了学,但总感觉忘得很快,水一水博客,把自己的笔记总结一下,顺便清理一下收藏夹,哈哈哈。

10.1 数论初步
一、欧几里得算法(辗转相除法)

  1. 最大公约数gcd(a, b)
    最小公倍数lcm(a, b)
  2. 紫书中介绍的是gcd的递归算法,即辗转相除法
    辗转相除法的关键在于如下恒等式:gcd(a, b)=gcd(b, a mod b),即cd(a, b)=gcd(b, a%b),不断向下取余,当b=0时即为边界,gcd(a, 0)=a
  3. 在证明利用gcd求lcm时提到了唯一分解定理
    唯一分解定理:一个数n肯定能被分解成n=p1^a1 * p2^a2 * . . .* pn^an(最后变成了质数相乘的方式,分解方式唯一)
    例如:36=4*9=2^3 * 3^2
    唯一分解定理入门
  4. 先求gcd,再求lcm,上模板:
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;
}

二、筛法

  1. 本来书中只介绍了埃氏筛,但查资料又发现了线性筛,一块总结出来
  2. 埃氏筛法(构建1~n的素数表)的思想:如果找到一个质数,那么这个质数的倍数都不是质数
    时间复杂度:O(nlogn)
    (摘抄的另一位博主的解释,很容易明白)
    比如2是质数,那么4,6,8,10,12…都不是质数
    然后看3是质数,那么6,9,12,15,18,21…都不是质数
    然后看4,4已经被2标记为合数了,所以跳过
    然后看5…这样一直筛下去
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;
    }
}
  1. 埃氏筛的缺陷:一个合数可能被筛多次,例如30,被2, 3,…筛过—>使每个合数只被筛选一次—>只用该合数的最小质因子来筛选—>欧拉筛(不重复,以埃氏筛为基础)
    时间复杂度:O(n)
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;//关键步骤,保证每个合数被它最小的质因数筛去
        }
    }
}
  1. 模板题:习10-4 UVA1644

三、扩展欧几里得算法

  1. 抄一抄度娘的定义:众所周知,有两个数a, b,对它们进行辗转相除法,可得它们的最大公约数,扩展欧几里得算法还能找到一对整数(x, y),即ax+by=gcd(a, b)的整数解,(x, y可能是负数或者0)

  2. 简言之,扩展欧几里得算法的作用:得到ax+by=gcd(a,b)的所有整数解

  3. 代码解释:前面提到,辗转相除法的关键在于如下恒等式:gcd(a, b)=gcd(b, a%b),
    不断向下取余,当b==0时即为边界,gcd(a, 0) = a = a * 1 + 0 * 0—>(x=1, y=0),这样我们就知道了最底层的一组解,再利用推导出来的公式(这里不懂得嘤)求出上面的解
    (注意在递归算法中,永远都是先得到下面一个状态的值)

  4. 附上一些链接,希望以后能完全理解
    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);
    }
}
  1. 得到一组解后,如何得到其他解呢?
    设a, b, c为任意整数,g=gcd(a, b),方程ax+by=g的一组解是(x0, y0),则当c是g的倍数时ax+by=c的一组解是(x0c/g, y0c/g);当c不是g的倍数时无整数解

  2. 模板题:例10-2 UVA12169

四、同余与模运算

  1. 公式:
    (a+b)%n=((a%n)+(b%n))%n
    (a-b)%n=((a%n)-(b%n)+n)%n //由于a%n有可能小于b%n,需要在结果上加上n
    ab%n=(a%n)(b%n)%n //积的取余等于取余的积的取余
  2. 大整数取模
    输入正整数m和n,输出 n mod m 的值,n<=10^100, m<=10^9
    首先,把大整数写成“自左向右”的形式:1234=((1*10+2)*10+3)*10+4,然后用前面的公式,每步取模
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);
  1. 幂取模
    (1)输入正整数a, n和m,输出 a^n%m 的值,a, n, m<=10^9
    (2)快速幂运算,以前遇到过一道题求a^b,用pow一直不对,在群里问求助dalao,被告知手写快速幂不就好了,那个时候还不懂快速幂是啥,自学的时候也没理解,后来又遇到这样的题,再去学就懂了,果然不要失去信心,慢慢学。。。
    a的b次方怎么求:pow(a, b)是数学头文件math.h里面有的函数,可是它返回值是double类型,数据有精度误差
    (3) 解释:在求解同余方程时,常常会遇到 a^b%c 的问题,根据模运算的分配律,对这个步骤进行简化(二分和递归)……例:求2^6, 即2^3 * 2^3; 求2^5, 即2*2^4
    b是奇数:a^b = a * a^(b-1)
    b是偶数:a^b = a^(b/2) * a^(b/2)
    理解了这些以后,递归就好了
    快速幂
//递归写法
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;
    }
}
  1. 同余和逆
    (1)a≡b(mod n):a和b除以n的余数相同 <=> a-b是n的整数倍
    (2)b=1时,ax≡1(mod n)的解称为a关于模n的逆,它类似于实数运算中的“倒数”概念。当gcd(a, n)=1时,该方程有唯一解,否则无解

五、补充知识

  1. 例10-1 UVA11582:斐波那契数列对任意数取模,其余数一定会出现循环 用到了快速幂
  2. 例10-5 UVA12716:
    (1)异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为: a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
    如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
    (2)根据 T xor T = 0, a xor b = c, T xor 0 = T
    => a xor b xor a = c xor a
    => b = a xor c
    (3)小结论:要使得 gcd(a, b)==a^b, 则 gcd(a, b)==a-b ==a^b
  3. << : 左移,二进制位移操作(相当于*2)
    1< b&1即b%2==1
    b>>=1即b=b>>1即b=b/2(右移,相当于/2)
  4. 科学计数法:
    1e5 == 1*(10^5)
    5e7 == 5*(10^7)

你可能感兴趣的:(笔记)