【问题标题】:python minmax using only recursionpython minmax 仅使用递归
【发布时间】:2019-04-10 11:01:12
【问题描述】:

我正在尝试构建一个接收列表并返回 (min, max) 元组的函数。

例如,

[2,1,4,9,4.5]

会回来

(1, 9)

我正在尝试仅使用递归并希望执行此任务而不使用其他使这变得非常容易的东西(例如 min()、max()、sort()、sorted()、loop..等)

到目前为止,我已经能够创建找到最大值的函数

def findmax(alist):
  if len(alist) <= 1:
    return tuple(alist)
  elif len(alist) == 2:
    if alist[0] >= alist[1]:
        return findmax([alist[0]])
    elif alist[0] <= alist[1]:
        return findmax([alist[1]])
  elif len(alist) > 2:
    if alist[0] >= alist[1]:
        return findmax([alist[0]] + alist[2:])
    elif alist[0] <= alist[1]:
        return findmax(alist[1:])

哪个

findmax([2,1,4,9,4.5])

返回

(9,)

还有一个找到最小值的函数(差别不大)

def findmin(alist):
  if len(alist) <= 1:
    return tuple(alist)
  elif len(alist) == 2:
    if alist[0] >= alist[1]:
        return findmin([alist[1]])
    elif alist[0] <= alist[1]:
        return findmin([alist[0]])
  elif len(alist) > 2:
    if alist[0] >= alist[1]:
        return findmin(alist[1:])
    elif alist[0] <= alist[1]:
        return findmin([alist[0]] + alist[2:])

哪个

findmin([2,1,4,9,4.5])

返回

(1,)

有没有办法只使用递归将这两个单独的函数合二为一,以便返回所需的结果

(1, 9)

任何帮助将不胜感激。

【问题讨论】:

    标签: python recursion minmax


    【解决方案1】:

    我发现这类问题往往比您预期的要简单。让递归完成工作:

    def find_min_max(a_list):
    
        if a_list:
            head, *tail = a_list
    
            if tail:
                minimum, maximum = find_min_max(tail)
    
                return [head, minimum][minimum < head], [head, maximum][maximum > head]
    
            return head, head
    
        return a_list
    

    用法

    >>> find_min_max([2, 1, 4, 9, 4.5])
    (1, 9)
    >>> find_min_max('elephant')
    ('a', 't')
    >>> 
    

    此解决方案特定于 Python 3,但可以轻松修改以兼容 Python 2 和 3。

    【讨论】:

      【解决方案2】:

      下面,minmax 使用连续传递样式表示。在这种风格中,就好像我们的状态变量是从以太中拉出来的。有关使用这种风格编写的其他程序的更多示例,请参阅this answer

      from math import inf
      
      def first (xs):
        return xs[0]
      
      def rest (xs):
        return xs[1:]
      
      def tuple2 (a, b):
        return (a, b)
      
      def minmax (xs = [], then = tuple2):
        if not xs:                 # base case: no `x`
          return then (inf, -inf)
        else:                      # inductive case: at least one `x`
          return minmax \
            ( rest(xs)
            , lambda a, b:
                then \
                 ( min (a, first (xs))
                 , max (b, first (xs))
                 )
            )
      
      print (minmax ([ 2, 1, 4, 9, 4.5 ]))
      # (1, 9)
      
      print (minmax ([]))
      # (inf, -inf)
      

      minmax 定义为

      def min (a, b)
        if a < b:
          return a
        else:
          return b
      
      def max (a, b)
        if a > b:
          return a
        else:
          return b
      

      【讨论】:

      • 您通过调用min()max() 实现了minmax(),OP 说不能使用!如果我们可以简单地调用min()max(),我们就不需要任何这个额外的逻辑:minmax = lambda x: (min(x), max(x))
      • @cdlane 将遍历x 两次。该算法不这样做。添加了简单的 minmax 定义。
      【解决方案3】:

      分别找到最大值或最小值很容易。困难的是通过递归调用找到最大值和最小值。 Tail recursion 正是为此(通过递归调用维护和更新变量的状态),通常写起来很简单:

      def findminmax(L):
          def inner(L1, min, max):
              if L1 == []:
                  return (min, max)
              elif L1[0] > max:
                  return inner(L1[1:], min, L1[0])
              elif L1[0] < min:
                  return inner(L1[1:], L1[0], max)
              else:
                  return inner(L1[1:], min, max)
          return inner(L[1:], L[0], L[0])
      
      findminmax([2,1,4,9,4.5])
      # => (1, 9)
      

      无需分配和花哨的列表索引。只需要最基本的列表操作。递归结构清晰且非常标准(明显见基本案例、归约和函数递归调用),代码也非常通俗易懂。

      更新

      处理字符串输入和空列表或字符串输入的一点修改:

      def findminmax(LS):
          def inner(LS1, min, max):
              if not LS1:
                  return (min, max)
              elif LS1[0] > max:
                  return inner(LS1[1:], min, LS1[0])
              elif LS1[0] < min:
                  return inner(LS1[1:], LS1[0], max)
              else:
                  return inner(LS1[1:], min, max)
          try:
              return inner(LS[1:], LS[0], LS[0])
          except IndexError:
              print("Oops! That was no valid input. Try again...")
      
      findminmax([2,1,4,9,4.5])
      # => (1, 9)
      
      findminmax([2])
      # => (2, 2)
      
      findminmax('txwwadga')
      # => ('a', 'x')
      
      findminmax('t')
      # => ('t', 't')
      
      findminmax([]) # empty list
      # => Oops! That was no valid input. Try again...
      
      findminmax('') # empty string
      # => Oops! That was no valid input. Try again...
      

      【讨论】:

      • 我很高兴阅读您的回答。这是一个非常好的解决方案。只使用了简单的基本操作。
      • 你是 inner() 嵌套函数检查 L1 是否为空,但你的外部 findminmax() 无法检查 L 是否为空,因此与 IndexError: list index out of range 一起死亡
      • @cdlane 感谢您的宝贵意见。我从你那里学到了一些重要的东西。答案现已更新。如果您有更好的想法,请随时编辑它
      【解决方案4】:

      你可以再添加一个def(读cmets):

      def f(l):
          return findmin(l)+findmax(l) # Also you can do: `(findmin(l)[0],findmax(l)[0])`
      

      现在调用它,做:

      print(f([2,1,4,9,4.5]))
      

      输出将是:

      (1, 9)
      

      【讨论】:

        【解决方案5】:

        您肯定使递归函数过于复杂。最小值和最大值都可以使用以下递归代码在元组中返回。

        my_list = [2,1,4,9,4.5]
        
        def recursive_min_max(list_a, pos, biggest, smallest):
            if pos != len(list_a) - 1:
                biggest_new = list_a[pos] if biggest == None else list_a[pos] if list_a[pos] > biggest else biggest
                smallest_new = list_a[pos] if smallest == None else list_a[pos] if list_a[pos] < smallest else smallest
                return recursive_min_max(list_a, pos + 1, biggest_new, smallest_new)
            return (biggest,smallest)
        
        
        print(recursive_min_max(my_list, 0, None, None))
        

        在每一步,当前列表项都与当前最大和最小元素进行比较。如果它们更大/更小,则当前值替换它们。

        【讨论】:

        • 如果您将此解决方案应用于列表[1],您应该得到(1, 1),但您得到的是(None, None)。如果将此解决方案应用于空列表,则会失败并显示IndexError: list index out of range
        • 逻辑好像有问题。如果我设置my_list = [112, 97],结果我会返回(112, 112)。将if pos != len(list_a) - 1 更改为简单的if pos &lt; len(list_a) 似乎有帮助。 return (biggest,smallest) 也是落后的。
        猜你喜欢
        • 2017-07-04
        • 1970-01-01
        • 2016-02-28
        • 2011-05-13
        • 1970-01-01
        • 2011-02-17
        • 2019-08-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多