zoukankan      html  css  js  c++  java
  • 浅谈关于欧几里得的一系列算法

    浅谈关于欧几里得的一系列算法

    --------- 这里有个叫分界线的家伙说,本章的所有讨论均在整数的范围中,所有除法都为带余除法o----------------

    朴素欧几里得算法

    又名辗转相除法,代码实现如下:

    int gcd(int a, int b) // a >= b 
    {  
          if(b == 0)    return a;  
          return gcd(b, a % b)  
    }
    

    想一想为什么可以这样计算?

    我们设

    (a=k_1m, b=k_2m ,gcd(a,b)=m, gcd(k_1,k_2)=1)

    很容易发现,当我们进行加减运算的时候,他们的和或者差一定是 (m) 的倍数,也就是说最大公约数不改变

    既然这样,那么当 (a mod b=0) 的时候可以发现此时的 (b) 一定就是最大公约数 (m)

    而我们为了造出这个特殊情况,我们可以像代码中展示的那样不停的迭代,由于我们始终保证了 (a>b) ,所以我们最后一定能得到我们想要的情况,从而得到 (gcd)

    扩展欧几里得算法

    这个算法的用处是用来解形如 (ax + by = c) 的不定方程

    首先,如果你进行过一点思考的话就为发现必须满足条件 (c mid gcd(a,b)) 原方程才有整数解

    因此,我们可以把原式变成一个更容易讨论的形式

    (ax+by=gcd(a,b) (a>=b))

    由于上面的思考,不难发现

    (ecause gcd(a,b)=gcd(b,a mod b))

    同上,我们依然讨论一下它的特殊情况,当 (b=0) 时,也就是 (ax + by=a) 时,显然 (x=1, y=0)

    那么我们要做的就是通过这个临界情况,反推出原方程的一组解

    $ herefore $ 我们假设

    (ax_1+by_1=gcd(a,b))

    (bx_2+(a mod b)y_2=gcd(b,a mod b))

    然后对这个形式做一些变形

    (Rightarrow ax_1+by_1=bx_2+(a mod b)y_2)

    (Rightarrow ax_1+by_1=bx_2+(a-frac{a}{b}b)y_2)

    (Rightarrow ax_1+by_1=b(x_2-(frac{a}{b})y_2)+ay_2)

    又由于恒等定理可得

    (x_1=y_2, y_1=x_2-(frac{a}{b})y_2)

    这样,我们就可以通过 (x_2, y_2) ,推断出 (x_1, y_1)

    同样的道理,我们也就这样迭代下去,就可以得出那组特殊的解了

    由于它的方法和朴素欧几里得算法有莫大的关联,所以我们可以在原来的代码上加上一些神秘的东西,变成扩展欧几里得算法

    int exgcd(int aaa, int bbb, int &x, int &y)  
    {  
        if(bbb == 0)  
        {  
            x = 1;  
            y = 0;  
            return aaa;  
        }  
        g = exgcd(bbb, aaa % bbb);  
        int temp = x; x = y; y = temp - aaa / bbb * y;  
        return g;  
    }
    

    类欧几里得算法

    这个算法其实可以衍生出好几种,接下来我们所讲解的是最基础,也是最经典的那一种

    (f(a,b,c,n)=sum_{i=0}^nlfloorfrac{ai+b}{c} floor)

    我们要求的这个函数其实可以换个角度来观察

    (f(a,b,c,n)=sum_{i=0}^nlfloorfrac{a}{c}i+frac{b}{c} floor)

    其实这个函数的几何意义是一次函数下 (xin [0,n]) 整点的个数(包含直线上的点,但是不包含 (y=0) 的点)

    所以我们就可以在这个思想上在进行一些变化

    (m=lfloorfrac{an+b}{c} floor)

    (f(a,b,c,n)=sum_{i=0}^n sum_{j=1}^m [frac{ai+b}{c}>=j])

    (f(a,b,c,n)=sum_{i=0}^n sum_{j=0}^{m-1} [frac{ai+b}{c}>=j+1])

    (f(a,b,c,n)=sum_{i=0}^n sum_{j=0}^{m-1} [ai>=cj+c-b])

    (f(a,b,c,n)=sum_{i=0}^n sum_{j=0}^{m-1} [ai>cj+c-b-1])

    (f(a,b,c,n)=sum_{i=0}^n sum_{j=0}^{m-1} [i>=(frac caj+frac{c-b-1}a)])

    (f(a,b,c,n)=sum_{j=0}^{m-1} [n-(frac caj+frac{c-b-1}a)])

    (f(a,b,c,n)=nm-sum_{j=0}^{m-1} [frac caj+frac{c-b-1}a])

    (f(a,b,c,n)=nm-f(c,c-b-1,a,m-1))

    通过这个变化,就可以递归求解了,时间复杂度和朴素欧几里得算法类似

    心如花木,向阳而生。
  • 相关阅读:
    如何巧妙着运用「位运算」来解决问题?
    一文读懂一台计算机是如何把数据发送给另一台计算机的
    Java集合与泛型中的几个陷阱,你掉进了几个?
    【链表问题】打卡10:将搜索二叉树转换成双向链表
    【链表问题】打卡9:将单链表的每K个节点之间逆序
    【链表问题】打卡8:复制含有随机指针节点的链表
    单例模式中的volatile关键字
    链表回文判断(基于链表反转)—Java实现
    设计模式之单例模式
    Spring的IoC与AOP的理解
  • 原文地址:https://www.cnblogs.com/LLppdd/p/8428349.html
Copyright © 2011-2022 走看看