【问题标题】:recursive backtracking - python. not returning value递归回溯 - python。不返回值
【发布时间】:2016-05-14 13:53:52
【问题描述】:

问题

我知道在我的函数的某个地方,我没有返回我应该返回的东西。

我正在返回递归调用,但似乎我没有返回“一路”

上下文

我正在对列表中的每个组合进行深度优先搜索。一旦达到达到某个条件的组合,我就想返回。

我正在维护我的组合的“状态”,并且正在回溯我应该在哪里(我认为)。

我做错了什么?

class Combo:
    def __init__(self, list):
       self.staples = list

Combo 有一个名为“staples”的属性,由一系列订书钉类组成。我想遍历决策树中的钉书钉列表以找到最佳数量。

在这种情况下,最佳数量是对列表中每个主要实例的数量求和,并作为 Combo 实例的属性存储/重新计算。

def IterateStaples(combo, target):        
  #Exit condition for combo dictionary
  if all(combo.diff[macro] < 2 for macro in combo.diff):    
    return combo;                            

  #iterate through all items in list
  for staple in combo.staples:                                  

    #Increment and calc conditions
    staple.increment()         
    combo.calcTotals()      
    combo.findDiff(target)

    #If exceeds target value, backtrack
    if combo.findConflict(target):                
      staple.decrement()
      combo.calcTotals()                
      combo.findDiff(target)              

    #Redundant exit condition to try and return
    elif all(combo.diff[macro] < 2 for macro in combo.diff):                                  
      return combo                

    #Recursive call
    else:        
      return IterateStaples(combo, target)
      staple.decrement()
      combo.calcTotals()
      combo.findDiff(target)

【问题讨论】:

  • 如果不知道combostaples 对象是什么,这段代码很难理解。
  • 很好的反馈,谢谢。我会编辑

标签: python recursion backtracking recursive-backtracking


【解决方案1】:

如果我正确理解了您的代码(这比平时更难,因为您没有显示您在 combostaple 上调用的大多数方法是什么),这应该是您想要的:

def IterateStaples(combo, target):        
    # base case
    if all(combo.diff[macro] < 2 for macro in combo.diff): # iterate on combo.diff.values()?
        return combo   # returning combo indicates success!

    for staple in combo.staples:
        staple.increment()                 # update state   
        combo.calcTotals()      
        combo.findDiff(target)

        if not combo.findConflict(target):  # skip recursing on invalid states
            result = IterateStaples(combo, target)    # recursive case
            if result is not None:      # if the recursion was successful, return the result
                return result

        staple.decrement()  # otherwise, undo the change to the state (backtrack)
        combo.calcTotals()     # these two lines might not be necessary when backtracking
        combo.findDiff(target) # since other branches will call them after staple.increment()

    return None # if we got to the end of the loop, explicitly return None to signal failure

末尾的return None 不是绝对必要的,因为如果您不返回任何其他内容,None 是默认返回值。我只是认为最好明确一点。

我按照您的代码在成功时返回 combo(并将其扩展到在失败时返回 None)。由于代码在原地改变了combo,您也可以返回True 表示成功(在函数顶部的基本情况)和False 表示失败(在函数的底部,结束后循环)。递归逻辑将传递True 结果,并在False 结果之后回溯。*调用者需要检查他们传入的 combo 实例以获取实际解决方案,如果他们得到 True 返回值:

combo = Combo(something)
if IterateStaples(combo, target):
    do_stuff(combo) # success!

【讨论】:

  • 嗯。我刚刚对其进行了测试,但它未能通过我的单元测试。似乎虽然它找到了解决方案,但变异的“组合”实例并不准确。将调试并及时更新
  • 将底座移至底部。初始化后,我立即退出,因为我没有计算diff(diff被初始化为0)
  • 我无法真正帮助您解决这些问题,因为我不知道calcTotalsfindDiffincrementdecrement 方法的作用。如果你想要一个完整的答案,你需要问一个完整的问题。
  • 哈哈没问题。我现在遇到算法超时的问题。如果我无法弄清楚,我会问一个更完整的问题! :)
【解决方案2】:

for 循环中的第一个 if 语句不会返回任何内容。它应该返回什么取决于您的算法逻辑:

#If exceeds target value, backtrack
if combo.findConflict(target):                
   staple.decrement()
   combo.calcTotals()                
   combo.findDiff(target)
   return SOMETHING

此外,最后 3 行永远不会被执行,它们在 return 语句之后。

【讨论】:

  • 因为我正在迭代一个集合并且决策树的这个分支对我没有用,我需要返回一些东西吗?我最初有一个函数调用,而不是最后 4 行的返回。不过,我现在正在尝试您的建议。
  • 决策树的这个分支对我没用”不适用于递归算法。如果迭代依赖于嵌套调用的结果,则该嵌套调用必须返回相关结果。
  • @Amit 您的回答使 for 循环无用,因为它总是在第一次迭代时退出函数
【解决方案3】:

通过在以下内容中加入辅助函数,我能够通过自己的测试用例:

这不是回溯吗?我用类似的方法实现了 N-queens

def IterateStaples(combo, target):        
  #iterate through all items in list
  bestCombo = []
  def helper(combo):
    for staple in combo.staples:                                      
      #Increment and calc conditions
      staple.increment()         
      combo.calcTotals()      
      combo.findDiff(target)    

      #If exceeds target value, backtrack
      if combo.findConflict(target):                      
        staple.decrement()
        combo.calcTotals()                
        combo.findDiff(target)                                        

      #Redundant exit condition to try and return
      elif all(combo.diff[macro] < 2 for macro in combo.diff):                                          
        bestCombo.append(deepcopy(combo))        
        return                

      #Recursive call
      else:        
        helper(combo)
        staple.decrement()
        combo.calcTotals()
        combo.findDiff(target)

  helper(combo)  

  return bestCombo

【讨论】: