【问题标题】:Calculate the extended gcd using a recursive function in Python在 Python 中使用递归函数计算扩展 gcd
【发布时间】:2012-09-22 13:14:31
【问题描述】:

给我函数gcd,定义如下:

def gcd(a, b):
    if (0 == a % b):
        return b
    return gcd(b, a%b)

现在我被要求编写一个递归函数gcd2(a,b),它返回一个包含三个数字(g, s, t) 的列表,其中g = gcd(a, b)g = s*a + t*b

这意味着您将在gcd(a, b) 函数中输入两个值(a and b)。它返回的值等于下一个函数中的g

这些相同的ab 值随后被调用到gcd2(a, b)。然后使用递归部分查找 s 和 t 的值,以便 g = s*a + t*b.

我不知道如何解决这个问题,因为我无法真正想象 "stopping-condition" 会是什么,或者我将通过递归循环来实际找到 @987654335 @ 和 t。谁能帮帮我?

【问题讨论】:

  • 为什么三参数递归调用的停止情况与两参数递归调用有任何不同?
  • 这个问题真的没有意义。 s 和 t 将有无穷多个解。
  • 您可能应该再读一遍这个问题,因为我们无事可做。请添加更多信息!
  • @LoSauer:注意作业标签现在是officially deprecated;不再需要用它标记问题。
  • @wim 确实有道理,你拥有哪一个并不重要。如果您有一对,则可以计算所有对。 en.wikipedia.org/wiki/B%C3%A9zout%27s_identity

标签: python recursion


【解决方案1】:

关键的见解是我们可以向后工作,在递归中为每个ab 找到st。假设我们有a = 21b = 15。我们需要使用几个值来完成每次迭代——abb % ac,其中a = c * b + a % b。首先,让我们考虑基本 GCD 算法的每一步:

21 = 1 * 15 + 6
15 = 2 * 6  + 3
6  = 2 * 3  + 0 -> end recursion

所以我们的 gcd (g) 是 3。一旦有了它,我们就确定了 6 和 3 的 st。为此,我们从 g 开始,用 @ 表示987654337@:

3  = 1 * 3 + -1 * 0

现在我们要消除 0 项。从基本算法的最后一行,我们知道0 = 6 - 2 * 3:

3 = 1 * 3 + -1 * (6 - 2 * 3)

简化,我们得到

3 = 1 * 3 + -1 * 6 + 2 * 3
3 = 3 * 3 + -1 * 6

现在我们交换条款:

3 = -1 * 6 + 3 * 3

所以我们有s == -1t == 3 用于a = 6b = 3。所以给定ab 的值,gcd2 应该返回(3, -1, 3)

现在我们通过递归退一步,我们想要消除第 3 项。从基本算法的倒数第二行,我们知道3 = 15 - 2 * 6。再次简化和交换(慢慢来,这样你可以清楚地看到步骤......):

3 = -1 * 6 + 3 * (15 - 2 * 6)
3 = -1 * 6 + 3 * 15 - 6 * 6
3 = -7 * 6 + 3 * 15
3 = 3 * 15 + -7 * 6

所以对于这个级别的递归,我们返回(3, 3, -7)。现在我们要消除 6 项。

3 = 3 * 15 + -7 * (21 - 1 * 15)
3 = 3 * 15 + 7 * 15 - 7 * 21
3 = 10 * 15 - 7 * 21
3 = -7 * 21 + 10 * 15

瞧,我们计算了 21 和 15 的 st

如此示意性地,递归函数将如下所示:

def gcd2(a, b):
    if (0 == a % b):
        # calculate s and t
        return b, s, t
    else:
        g, s, t = gcd2(b, a % b)
        # calculate new_s and new_t
        return g, new_s, new_t

请注意,出于我们的目的,使用稍微不同的基本情况可以简化事情:

def gcd2(a, b):
    if (0 == b):
        return a, 1, -1
    else:
        g, s, t = gcd2(b, a % b)
        # calculate new_s and new_t
        return g, new_s, new_t

【讨论】:

    【解决方案2】:

    基本情况(停止条件)是:

    if a%b == 0:
        # a = b*k for the integer k=a/b
        # rearranges to b = -1*a + (k+1)*b
        #             ( g =  s*a + t*b )
        return (b, -1, a/b+1) # (g, s, t)
    

    不过练习是重写递归部分:

    g1, s1, t1 = gcd(b, a%b) # where g1 = s1*b + t1*(a%b)
    g, s, t = ???            # where g = s*a + t*b
    return (g, s, t)
    

    g1s1t1而言...归结为用ab重写a%b

    【讨论】:

      【解决方案3】:

      “在 Python 中编写递归函数”,至少在 CPython 中,为此哭泣:注意 http://docs.python.org/library/sys.html#sys.getrecursionlimit。在我看来,这是对这个问题最重要的答案之一。请自己对此主题进行一些研究。另外,这个帖子可能很有见地:Python: What is the hard recursion limit for Linux, Mac and Windows?

      总之,尽可能在 Python 中尝试使用迭代而不是递归方法。

      【讨论】:

      • 我更喜欢迭代方法,但不幸的是这是一个家庭作业,并且指定了递归方法。
      • 只要你有界,递归就可以了,我认为 GCD 至少是O(log(n))。但仍然很好记住。
      【解决方案4】:

      它基于欧几里得算法,使用更好的while循环继续递归更好,更少执行

      def gcd(m,n):
      #assume m>= n
      if m <n:
          (m,n) = (n,m)
      if (m%n) == 0:
          return(n)
      else:
          diff =m-n
          #diff >n ?Possible!
          return(gcd(max(n,diff),min(n,diff)))
      

      while循环会更好

      def gcd(m,n):
      if m<n :
          (m,n) =(n,m)
      while (m%n) !=0:
          diff =m-n
          (m,n) =(max(n,diff),min(n,diff))
      return(n)
      

      【讨论】:

        猜你喜欢
        • 2016-12-05
        • 1970-01-01
        • 2019-01-01
        • 2023-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-14
        • 1970-01-01
        相关资源
        最近更新 更多