Skip to main content
require './widget' require './requirements' require 'benchmark' class BranchBoundSearch @best_score=nil@best_score = nil @best_combos=[]@best_combos = [] def initialize(scorer, main_requirement_label, all_possible) @scorer=scorer@scorer = scorer @main_requirement_label=main_requirement_label@main_requirement_label = main_requirement_label @all_possible=all_possible@all_possible = all_possible end #Given# Given a widget_array first calculate it's score, and add it as a branch in the #list# list of branches based on the quality of it's score. Better scores are first. #Branches# Branches that do not meet the size requirement are not added to the branches #array# array. This effectively kills that branch. def add_branch(widget_array, branches) score_hash=@scorerscore_hash = @scorer.get_score_hash(widget_array) score=@scorerscore = @scorer.get_total_score(score_hash) added=falseadded = false branches_index=0branches_index = 0  if score_hash[@main_requirement_label] >= 0 while not added and branches_index < branches.length do if @scorer.get_better_score(score, branches[branches_index][1]) >= 0 branches.insert(branches_index, [score_hash,score,widget_array]) added=trueadded = true end branches_index+=1branches_index += 1 end   if not added branches.push([score_hash, score,widget_array]) end end end #Branch# Branch and bound recursive search. Provided an in array which represents the #node# node which will be branched off of. Roughly, the function first creates a list #of# of potential branches which are ordered by the quality of their score. #Potential# Potential branches are then looped through, if a potential branch is viable the #function# function is called again with it as the root node. This continues until #exhaustion# exhaustion of all possiblities. def branch_and_bound(in_array) branches=[]branches = [] @all_possible.each do |widget| branch_array=in_array+[widget]branch_array = in_array + [widget] add_branch(branch_array, branches) end branches.each do |branch| score_hash=branch[0]score_hash = branch[0] score=branch[1]score = branch[1] widget_array=branch[2]widget_array = branch[2] score_comparison=@scorerscore_comparison = @scorer.get_better_score(score, @best_score)   if score_comparison == 0 @best_combos.push(widget_array) continue_branch_investigation=truecontinue_branch_investigation = true elsif score_comparison == 1 @best_combos=[widget_array]@best_combos = [widget_array] @best_score=score@best_score = score continue_branch_investigation=truecontinue_branch_investigation = true elsif score > 0 continue_branch_investigation=truecontinue_branch_investigation = true end   if continue_branch_investigation branch_and_bound(widget_array) end end end def get_best_score() return @best_score, @best_combos   end end 
best_score_90=nilbest_score_90 = nil best_set_90=[]best_set_90 = [] best_score_90_bb=nilbest_score_90_bb = nil best_set_90_bb=[]best_set_90_bb = [] best_score_400=nilbest_score_400 = nil best_set_400=[]best_set_400 = [] best_score_400_bb=nilbest_score_400_bb = nil best_set_400_bb=[]best_set_400_bb = [] time=Benchmark.bm(7) do |x| x.report("brute[90]:") do scorer=Scorerscorer = Scorer.new(requirements_90, :size) brute=BruteSearchbrute = BruteSearch.new(scorer, :size) possible_combos=brutepossible_combos = brute.enumerate_possible([], all_widgets.dup) best_score_90,best_set_90=brute best_set_90 = brute.get_best_score end   x.report("brute[400]:") do scorer=Scorerscorer = Scorer.new(requirements_400, :size) brute=BruteSearchbrute = BruteSearch.new(scorer, :size) possible_combos=brutepossible_combos = brute.enumerate_possible([], all_widgets.dup) best_score_400,best_set_400=brute best_set_400 = brute.get_best_score end   x.report("b&b[90]:") do scorer=Scorerscorer = Scorer.new(requirements_90, :size) bb=BranchBoundSearchbb = BranchBoundSearch.new(scorer, :size, all_widgets) bb.branch_and_bound([]) best_score_90_bb,best_set_90_bb=bb best_set_90_bb = bb.get_best_score end x.report("b&b[400]:") do scorer=Scorerscorer = Scorer.new(requirements_400, :size) bb=BranchBoundSearchbb = BranchBoundSearch.new(scorer, :size, all_widgets) bb.branch_and_bound([]) best_score_400_bb,best_set_400_bb=bb best_set_400_bb = bb.get_best_score end end puts "Best set [90] brute: "+" + widget_array_to_s(best_set_90) puts "Score[90] brute: "+best_score_90" + best_score_90.to_s 
require './widget' require './requirements' require 'benchmark' class BranchBoundSearch @best_score=nil @best_combos=[] def initialize(scorer,main_requirement_label,all_possible) @scorer=scorer @main_requirement_label=main_requirement_label @all_possible=all_possible end #Given a widget_array first calculate it's score, and add it as a branch in the #list of branches based on the quality of it's score. Better scores are first. #Branches that do not meet the size requirement are not added to the branches #array. This effectively kills that branch. def add_branch(widget_array,branches) score_hash=@scorer.get_score_hash(widget_array) score=@scorer.get_total_score(score_hash) added=false branches_index=0 if score_hash[@main_requirement_label] >= 0 while not added and branches_index < branches.length do if @scorer.get_better_score(score,branches[branches_index][1]) >= 0 branches.insert(branches_index,[score_hash,score,widget_array]) added=true end branches_index+=1 end if not added branches.push([score_hash,score,widget_array]) end end end #Branch and bound recursive search. Provided an in array which represents the #node which will be branched off of. Roughly, the function first creates a list #of potential branches which are ordered by the quality of their score. #Potential branches are then looped through, if a potential branch is viable the #function is called again with it as the root node. This continues until #exhaustion of all possiblities. def branch_and_bound(in_array) branches=[] @all_possible.each do |widget| branch_array=in_array+[widget] add_branch(branch_array,branches) end branches.each do |branch| score_hash=branch[0] score=branch[1] widget_array=branch[2] score_comparison=@scorer.get_better_score(score,@best_score) if score_comparison == 0 @best_combos.push(widget_array) continue_branch_investigation=true elsif score_comparison == 1 @best_combos=[widget_array] @best_score=score continue_branch_investigation=true elsif score > 0 continue_branch_investigation=true end if continue_branch_investigation branch_and_bound(widget_array) end end end def get_best_score() return @best_score,@best_combos   end end 
best_score_90=nil best_set_90=[] best_score_90_bb=nil best_set_90_bb=[] best_score_400=nil best_set_400=[] best_score_400_bb=nil best_set_400_bb=[] time=Benchmark.bm(7) do |x| x.report("brute[90]:") do scorer=Scorer.new(requirements_90,:size) brute=BruteSearch.new(scorer,:size) possible_combos=brute.enumerate_possible([],all_widgets.dup) best_score_90,best_set_90=brute.get_best_score end x.report("brute[400]:") do scorer=Scorer.new(requirements_400,:size) brute=BruteSearch.new(scorer,:size) possible_combos=brute.enumerate_possible([],all_widgets.dup) best_score_400,best_set_400=brute.get_best_score end x.report("b&b[90]:") do scorer=Scorer.new(requirements_90,:size) bb=BranchBoundSearch.new(scorer,:size,all_widgets) bb.branch_and_bound([]) best_score_90_bb,best_set_90_bb=bb.get_best_score end x.report("b&b[400]:") do scorer=Scorer.new(requirements_400,:size) bb=BranchBoundSearch.new(scorer,:size,all_widgets) bb.branch_and_bound([]) best_score_400_bb,best_set_400_bb=bb.get_best_score end end puts "Best set [90] brute: "+ widget_array_to_s(best_set_90) puts "Score[90] brute: "+best_score_90.to_s 
require './widget' require './requirements' require 'benchmark' class BranchBoundSearch @best_score = nil @best_combos = [] def initialize(scorer, main_requirement_label, all_possible) @scorer = scorer @main_requirement_label = main_requirement_label @all_possible = all_possible end # Given a widget_array first calculate it's score, and add it as a branch in the # list of branches based on the quality of it's score. Better scores are first. # Branches that do not meet the size requirement are not added to the branches # array. This effectively kills that branch. def add_branch(widget_array, branches) score_hash = @scorer.get_score_hash(widget_array) score = @scorer.get_total_score(score_hash) added = false branches_index = 0  if score_hash[@main_requirement_label] >= 0 while not added and branches_index < branches.length do if @scorer.get_better_score(score, branches[branches_index][1]) >= 0 branches.insert(branches_index, [score_hash,score,widget_array]) added = true end branches_index += 1 end   if not added branches.push([score_hash, score,widget_array]) end end end # Branch and bound recursive search. Provided an in array which represents the # node which will be branched off of. Roughly, the function first creates a list # of potential branches which are ordered by the quality of their score. # Potential branches are then looped through, if a potential branch is viable the # function is called again with it as the root node. This continues until # exhaustion of all possiblities. def branch_and_bound(in_array) branches = [] @all_possible.each do |widget| branch_array = in_array + [widget] add_branch(branch_array, branches) end branches.each do |branch| score_hash = branch[0] score = branch[1] widget_array = branch[2] score_comparison = @scorer.get_better_score(score, @best_score)   if score_comparison == 0 @best_combos.push(widget_array) continue_branch_investigation = true elsif score_comparison == 1 @best_combos = [widget_array] @best_score = score continue_branch_investigation = true elsif score > 0 continue_branch_investigation = true end   if continue_branch_investigation branch_and_bound(widget_array) end end end def get_best_score() return @best_score, @best_combos end end 
best_score_90 = nil best_set_90 = [] best_score_90_bb = nil best_set_90_bb = [] best_score_400 = nil best_set_400 = [] best_score_400_bb = nil best_set_400_bb = [] time=Benchmark.bm(7) do |x| x.report("brute[90]:") do scorer = Scorer.new(requirements_90, :size) brute = BruteSearch.new(scorer, :size) possible_combos = brute.enumerate_possible([], all_widgets.dup) best_score_90, best_set_90 = brute.get_best_score end   x.report("brute[400]:") do scorer = Scorer.new(requirements_400, :size) brute = BruteSearch.new(scorer, :size) possible_combos = brute.enumerate_possible([], all_widgets.dup) best_score_400, best_set_400 = brute.get_best_score end   x.report("b&b[90]:") do scorer = Scorer.new(requirements_90, :size) bb = BranchBoundSearch.new(scorer, :size, all_widgets) bb.branch_and_bound([]) best_score_90_bb, best_set_90_bb = bb.get_best_score end x.report("b&b[400]:") do scorer = Scorer.new(requirements_400, :size) bb = BranchBoundSearch.new(scorer, :size, all_widgets) bb.branch_and_bound([]) best_score_400_bb, best_set_400_bb = bb.get_best_score end end puts "Best set [90] brute: " + widget_array_to_s(best_set_90) puts "Score[90] brute: " + best_score_90.to_s 
Source Link
Mike
  • 205
  • 1
  • 5

Ruby Branch and Bound Algorithm implementation?

I am relatively new to Ruby and am trying to get a handle on making my code more Ruby-esque. At the same time I am experimenting with the branch and bound algorithm to solve a problem that I outline in this blog post. I would also welcome any feedback on the quality of my implementation. The full source code is on my github here, but here is the code for the branch bound class:

require './widget' require './requirements' require 'benchmark' class BranchBoundSearch @best_score=nil @best_combos=[] def initialize(scorer,main_requirement_label,all_possible) @scorer=scorer @main_requirement_label=main_requirement_label @all_possible=all_possible end #Given a widget_array first calculate it's score, and add it as a branch in the #list of branches based on the quality of it's score. Better scores are first. #Branches that do not meet the size requirement are not added to the branches #array. This effectively kills that branch. def add_branch(widget_array,branches) [email protected]_score_hash(widget_array) [email protected]_total_score(score_hash) added=false branches_index=0 if score_hash[@main_requirement_label] >= 0 while not added and branches_index < branches.length do if @scorer.get_better_score(score,branches[branches_index][1]) >= 0 branches.insert(branches_index,[score_hash,score,widget_array]) added=true end branches_index+=1 end if not added branches.push([score_hash,score,widget_array]) end end end #Branch and bound recursive search. Provided an in array which represents the #node which will be branched off of. Roughly, the function first creates a list #of potential branches which are ordered by the quality of their score. #Potential branches are then looped through, if a potential branch is viable the #function is called again with it as the root node. This continues until #exhaustion of all possiblities. def branch_and_bound(in_array) branches=[] @all_possible.each do |widget| branch_array=in_array+[widget] add_branch(branch_array,branches) end branches.each do |branch| score_hash=branch[0] score=branch[1] widget_array=branch[2] [email protected]_better_score(score,@best_score) if score_comparison == 0 @best_combos.push(widget_array) continue_branch_investigation=true elsif score_comparison == 1 @best_combos=[widget_array] @best_score=score continue_branch_investigation=true elsif score > 0 continue_branch_investigation=true end if continue_branch_investigation branch_and_bound(widget_array) end end end def get_best_score() return @best_score,@best_combos end end 

I'm also concerned about this specific code block:

best_score_90=nil best_set_90=[] best_score_90_bb=nil best_set_90_bb=[] best_score_400=nil best_set_400=[] best_score_400_bb=nil best_set_400_bb=[] time=Benchmark.bm(7) do |x| x.report("brute[90]:") do scorer=Scorer.new(requirements_90,:size) brute=BruteSearch.new(scorer,:size) possible_combos=brute.enumerate_possible([],all_widgets.dup) best_score_90,best_set_90=brute.get_best_score end x.report("brute[400]:") do scorer=Scorer.new(requirements_400,:size) brute=BruteSearch.new(scorer,:size) possible_combos=brute.enumerate_possible([],all_widgets.dup) best_score_400,best_set_400=brute.get_best_score end x.report("b&b[90]:") do scorer=Scorer.new(requirements_90,:size) bb=BranchBoundSearch.new(scorer,:size,all_widgets) bb.branch_and_bound([]) best_score_90_bb,best_set_90_bb=bb.get_best_score end x.report("b&b[400]:") do scorer=Scorer.new(requirements_400,:size) bb=BranchBoundSearch.new(scorer,:size,all_widgets) bb.branch_and_bound([]) best_score_400_bb,best_set_400_bb=bb.get_best_score end end puts "Best set [90] brute: "+ widget_array_to_s(best_set_90) puts "Score[90] brute: "+best_score_90.to_s 

Thanks for the help!