【问题标题】:python recursion combinationpython递归组合
【发布时间】:2014-05-25 04:14:48
【问题描述】:

如何编写一个计算函数:

C(n,k)= 1                          if k=0
        0                          if n<k
        C(n-1,k-1)+C(n-1,k)     otherwise

到目前为止我有:

def choose(n,k):
if k==0:
   return 1
elif n<k:
   return 0
else:

【问题讨论】:

    标签: python recursion combinations


    【解决方案1】:

    假设您的问题中缺少的操作数是减法运算符(感谢 lejlot),这应该是答案:

    def choose(n,k):
      if k==0:
         return 1
      elif n<k:
         return 0
      else:
         return choose(n-1, k-1) + choose(n-1, k)
    

    请注意,在大多数 Python 系统上,max depth or recursion limit 只有 1000。之后它将引发异常。您可能需要通过将此递归函数转换为迭代函数来解决此问题。

    这是一个迭代函数示例,它使用堆栈来模拟递归,同时避免 Python 的最大递归限制:

    def choose_iterative(n, k):
      stack = []
      stack.append((n, k))
      combinations = 0
    
      while len(stack):
        n, k = stack.pop()
        if k == 0:
          combinations += 1
        elif n<k:
          combinations += 0 #or just replace this line with `pass`
        else:
          stack.append((n-1, k))
          stack.append((n-1, k-1))
    
      return combinations    
    

    【讨论】:

    • 加法递归不会结束。另外,C 未定义
    • 有人正在为未定义的 C 函数编辑我的帖子 :(
    • 最后的语句不应该是C(n-1,k-1)+C(n-1,k),应该是C(n 1,k 1)+ C(n 1,k),意味着它将包含最后一个集合项的k项集合的计数和不包含最后一个集合项的k项集合的计数分开。
    【解决方案2】:

    从指数时间到线性时间的改进

    到目前为止给出的所有答案都在 指数时间 O(2<sup>n</sup>)。但是,可以通过更改一行代码使其在 O(k) 中运行。


    解释:

    指数运行时间的原因是每次递归都用这行代码将问题分成重叠的子问题(see Ideone here):

    def choose(n, k):
        ...
        return choose(n-1, k-1) + choose(n-1, k)
    

    要了解为什么这会如此糟糕,请考虑choose(500, 2) 的示例。 500 choose 2的数值为500*499/2;但是,使用上面的递归需要250499 递归调用来计算它。显然,这太过分了,因为只需要 3 次操作。

    要将其改进为线性时间,您需要做的就是选择一个不同的递归,它不会分成两个子问题(wikipedia 上有很多子问题)。

    例如下面的递归是等价的,但只使用3递归调用来计算choose(500,2)see Ideone here):

    def choose(n,k):
        ...
        return ((n + 1 - k)/k)*choose(n, k-1)
    

    改进的原因是每个递归只有一个子问题,每次调用都会将k 减少1。这意味着我们保证此递归只会采用k + 1 递归或O(k)。这对于更改一行代码来说是一个巨大的改进!

    如果您想更进一步,您可以利用“n 选择 k”中的对称性来确保 k &lt;= n/2 (see Ideone here):

    def choose(n,k):
        ...
        k = k if k <= n/2 else n - k            # if k > n/2 it's equivalent to k - n
        return ((n + 1 - k)/k)*choose(n, k-1)
    

    【讨论】:

      【解决方案3】:

      来自维基百科的解决方案 (http://en.wikipedia.org/wiki/Binomial_coefficient)

      def choose(n, k):
          if k < 0 or k > n:
              return 0
          if k > n - k: # take advantage of symmetry
              k = n - k
          if k == 0 or n <= 1:
              return 1
          return choose(n-1, k) + choose(n-1, k-1)
      

      【讨论】:

        【解决方案4】:

        您正在尝试计算从 n 个元素中选择 k 个选项的数量:

        def choose(n,k):
            if k == 0: 
               return 1 # there's only one option to choose zero items out of n
            elif n < k:
               return 0 # there's no way to choose k of n when k > n
            else:
                # The recursion: you can do either
                # 1. choose the n-th element and then the rest k-1 out of n-1
                # 2. or choose all the k elements out of n-1 (not choose the n-th element)
                return choose(n-1, k-1) + choose(n-1, k)
        

        【讨论】:

          【解决方案5】:

          就这样

          def choose(n,k):
              if k==0:
                  return 1
              elif n<k:
                  return 0
              else:
                  return choose(n-1,k-1)+choose(n-1,k)
          

          编辑

          这是一种简单的方法,为了高效,请查看维基百科和spencerlyon2 answer

          【讨论】:

          • spencerlyon2 的回答也没有效率——它将问题减半,但在“n”的大小上仍然是指数级的。如果您有兴趣,请查看我发布的解决方案。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-13
          相关资源
          最近更新 更多