2017-01-19 5 views
0

デプスファーストブランチとバウンドを使ってナップザック問題を解決するために、次のSwift 3プログラムを作成しました。残念ながら、再帰的です。それは20項目までうまく動作します。しかし、アイテム数(30以上)が増えると、スタックオーバーフローが発生します。 (〜5400レベルの深さ)。スタックオーバーフローを避けるために再帰的ブランチとバインドされたツリーノードのトラバーサルを変更するにはどうすればよいですか?

非再帰バージョンに変更するにはどうすればよいですか?私は4000のような一定の限界に再帰の深さを制限するために管理

public enum BBState { 
case created 
case active 
case death 
} 

public enum Direction { 
case left 
case right 
} 

public class BBTreeNode { 
    public var artifact: Artifact 
    public var nodeState: BBState 
    public var level: Int 
    public var path = [Direction]() 
    public var weightSoFar: Int = 0 
    public var optimumTotalValue: Int = 0 
    public var estimatedOptTotalValue: Double = 0 
    public weak var parentNode: BBTreeNode? 
    public var leftNode: BBTreeNode? 
    public var rightNode: BBTreeNode? 
    static var currentCompletedOptimumValues: Int = 0 
    static var currentCompletedEstimatedOptimumValues: Double = 0 
    static var currentOptimumPath = [Direction]() 

// Initialization 
public convenience init(artifact: Artifact) { 
    self.init(artifact: artifact, level: 0, path: [], parent: nil, left: nil, right: nil) 
} 

public init(artifact: Artifact, level: Int, path: [Direction], parent: BBTreeNode?, left: BBTreeNode?, right: BBTreeNode?) { 
    self.artifact = artifact 
    self.nodeState = .created 
    self.level = level 
    self.path = path 
    self.parentNode = parent 
    self.leftNode = left 
    self.rightNode = right 
} 


// BBTree 
private func createRootAndInitiateBB(artifacts: [Artifact]) { 
    // return the optimum value from Depth-First branch and bound 
    let maxLevel = artifacts.count - 1 
    print("Max Level: \(maxLevel)") 
    // create dummy Root to start 
    let root = BBTreeNode(artifact: Artifact(value: 0, weight: 0)) 
    root.estimatedOptTotalValue = Knapsack.calculateEstimatedOptimumValue(availableArtifacts: artifacts) 
    root.nodeState = .active 
    root.weightSoFar = Knapsack.capacity 
    // loop here for each artifact# - selected(left)/not(right) but going recursive to left until 
    // we have death node, then backtrack 

    func depthFirstTraversal(bbNode: BBTreeNode?, level: Int) { 
     guard let bbNode = bbNode // not nil 
      else { return } 

     guard level <= maxLevel 
      else { return } 

     print("Currently on path: \(bbNode.path)") 

     // switch to active is last state was created, else ignore 
     bbNode.nodeState = bbNode.nodeState == .created ? .active : bbNode.nodeState 

     // stop traverse down and traceback if calculated optimumValue < currentCompletedOptimumValue, 
     // or current weightSoFar is 0 or less than 0 
     // move up to parent 
     if bbNode.estimatedOptTotalValue < BBTreeNode.currentCompletedEstimatedOptimumValues || 
      bbNode.weightSoFar < 0 { 
      print("Case for estimatedOptTotalValue: \(bbNode.estimatedOptTotalValue) < currentCompletedEstimatedOptimumValues: \(BBTreeNode.currentCompletedEstimatedOptimumValues)") 
      print("Or weight: \(bbNode.weightSoFar) < 0") 
      bbNode.nodeState = .death 
      // remove references to children 
      bbNode.leftNode = nil 
      bbNode.rightNode = nil 
      depthFirstTraversal(bbNode: bbNode.parentNode, level: bbNode.level - 1) 
     } else if (bbNode.leftNode?.nodeState == .death && 
       bbNode.rightNode?.nodeState == .death) || level == maxLevel { 

      print("Case for no more path available. Need to backtrack. ") 
      print("Current level: \(level)") 
      // stop, record and traceback if at maxLevel or when both children are death 
      if level == maxLevel && bbNode.estimatedOptTotalValue > BBTreeNode.currentCompletedEstimatedOptimumValues { 
       BBTreeNode.currentCompletedEstimatedOptimumValues = bbNode.estimatedOptTotalValue 
       BBTreeNode.currentCompletedOptimumValues = bbNode.optimumTotalValue 
       BBTreeNode.currentOptimumPath = bbNode.path 
       print("blah...") 
       print("Candidate for optimum: \(bbNode.path)") 
       print("Completed optimum path: \(BBTreeNode.currentCompletedOptimumValues)") 
       print("Estimated optimum value: \(BBTreeNode.currentCompletedEstimatedOptimumValues)") 
      } 
      bbNode.nodeState = .death 
      // remove references to children 
      bbNode.leftNode = nil 
      bbNode.rightNode = nil 
      let _ = path.popLast() 
      depthFirstTraversal(bbNode: bbNode.parentNode, level: bbNode.level - 1) 
     } else if bbNode.leftNode == nil { 
      // create left child 
      print("create left child node") 
      let childLeftNode = createBBNode(leftChild: true, parent: bbNode, path: bbNode.path + [.left]) 
      bbNode.leftNode = childLeftNode 
      depthFirstTraversal(bbNode: childLeftNode, level: childLeftNode.level) 
     } else if bbNode.rightNode == nil { 
      // create right child 
      print("create right child node") 
      let childRightNode = createBBNode(leftChild: false, parent: bbNode, path: bbNode.path + [.right]) 
      bbNode.rightNode = childRightNode 
      depthFirstTraversal(bbNode: childRightNode, level: childRightNode.level) 
     } 

    } 

    func createBBNode(leftChild: Bool, parent: BBTreeNode, path: [Direction]) -> BBTreeNode { 
     let level = parent.level + 1 
     let artifact = artifacts[level] 
     let newBBNode = BBTreeNode(artifact: artifact, level: level, path: path, parent: parent, left: nil, right: nil) 
     if leftChild { 
      newBBNode.optimumTotalValue = parent.optimumTotalValue + artifact.value 
      newBBNode.estimatedOptTotalValue = parent.estimatedOptTotalValue 
      newBBNode.weightSoFar = parent.weightSoFar - artifact.weight 
     } else { 
      // right direction, we don't pick this item 
      // Artifact is a struct, artifacts is array of Artifact, so we don't need to write deep copy 
      var artifactsWithItemsRemoved = artifacts 

      print("Current artifacts before any removal: \(artifactsWithItemsRemoved)") 
      print("for path \(newBBNode.path) we are going to remove artifacts...") 
      // first remove the dummy artifact 
      artifactsWithItemsRemoved.remove(at: 0) 
      // now the real artifacts 
      var indexOfItemForRemoval = [Int]() 
      for (index,direction) in path.enumerated() { 
       if direction == .right { 
        indexOfItemForRemoval.append(index) 
       } 
      } 
      // actual removal, need to reverse the array index to avoid out of range index 
      for idx in indexOfItemForRemoval.reversed() { 
       artifactsWithItemsRemoved.remove(at: idx) 
      } 

      print("Artifacts with items removed: \(artifactsWithItemsRemoved)") 

      newBBNode.optimumTotalValue = parent.optimumTotalValue 
      newBBNode.estimatedOptTotalValue = Knapsack.calculateEstimatedOptimumValue(availableArtifacts: artifactsWithItemsRemoved) 
      print("New estimatedOptValue for \(newBBNode.path) is \(newBBNode.estimatedOptTotalValue)") 
      // no weight deduction if we don't pick 
      newBBNode.weightSoFar = parent.weightSoFar 
     } 
     return newBBNode 
    } 

    depthFirstTraversal(bbNode: root, level: 0) 
} 

public func findOptimumValueAndPath(artifacts: [Artifact]) -> (Int,[Direction], Double) { 
    createRootAndInitiateBB(artifacts: artifacts) 
    return (BBTreeNode.currentCompletedOptimumValues, BBTreeNode.currentOptimumPath, BBTreeNode.currentCompletedEstimatedOptimumValues) 
} 

} 

====更新===

は、カウンタリーチ制限一度、私はcurrentNodeの情報を呼び出し側に返さ。次に、currentNodeを使って新しい深層スタックを使って 'depthFirstTraversal'をもう一度呼び出します。ここで

は、私はそれを行う方法は次のとおりです。呼び出し元に

  1. 移動createRootAndInitiateBBとfindOptimumValueAndPath。

    静的depthFirstTraversal FUNC(bbNode:BBTreeNode、レベル:INT、recursiveCount:INT、完全completeFlag:ブールINOUT、currentProcessedNode:BBTreeNode INOUT)

  2. 一部

  3. 更新depthFirstTraversalは、このような関数シグネチャを有すること他のリファクタリング作業でカウンタ数を維持し、いくつかのバグを修正しました。 (例えば、parentNodeは弱くあってはならず、そうでなければ呼び出し側に戻り、currentNodeの親はnilになり、より高いノードにバックトラックする能力を失う)。ここで

答えて

関連する問題