尝试在Ruby中动态创建嵌套循环

时间:2013-09-27 20:21:42

标签: ruby recursion nested-loops

我目前有以下方法:

def generate_lineups(max_salary)
  player_combos_by_position = calc_position_combinations
  lineups = []

  player_combos_by_position[:qb].each do |qb_set|
    unless salary_of(qb_set) > max_salary
      player_combos_by_position[:rb].each do |rb_set|
        unless salary_of(qb_set, rb_set) > max_salary
          lineups << create_team_from_sets(qb_set, rb_set)
        end
      end
    end
  end
  return lineups
end

player_combos_by_position是一个哈希,其中包含按位置键入的玩家分组:

{ qb: [[player1, player2], [player6, player7]], rb: [[player3, player4, player5], [player8, player9, player10]] }

salary_of()获取一组球员并计算他们的总薪水。

create_team_from_sets()需要一组玩家并返回一个新的玩家团队

理想情况下,我想删除硬编码的嵌套循环,因为我不知道哪些位置可用。我认为递归是答案,但我很难绕过解决方案。任何想法将不胜感激。

有些答案建议使用Array#product。这通常是一个优雅的解决方案,但我正在处理非常大的数据集(大约有161,000个WR组合和大约5000个RB组合单独形成)。在我的循环中,我使用unless salary_of(qb_set, rb_set) > max_salary检查以避免进行不必要的计算,因为这样做了很多。我无法使用Array#product执行此操作,因此组合需要很长时间才能组合在一起。我正在寻找早期排除组合并节省计算机周期。

2 个答案:

答案 0 :(得分:1)

您可以使用Array#product获取所有可能的阵容,然后选择预算范围内的阵容。这允许可变数量的职位。

first_pos, *rest = player_combos_by_position.values

all_lineups =  first_pos.product(*rest)
#=> all possible lineups

lineups = all_lineups.
          # select lineups within budget
          select{|l| salary_of(*l) <= max_salary}.
          # create teams from selected lineups
          map{|l| create_team_from_sets(*l) }

其他选项:递归方法(未经测试但应该让您入门)

def generate_lineups(player_groups,max_salary)

   first, *rest = player_groups

   lineups = []
   first.each do |player_group|
        next if salary_of(player_group) > max_salary
        if rest.blank?
           lineups << player_group
        else
           generate_lineups(rest,max_salary).each do |lineup|
               new_lineup = create_team_from_sets(player_group, *lineup)
               lineups << new_lineup unless salary_of(*new_lineup) > max_salary
           end
        end
   end
  return lineups
end

用法:

lineups = generate_lineups(player_combos_by_position.values,max_salary)

答案 1 :(得分:1)

阅读完编辑后,我看到了您的问题。在这里,我修改了我的代码,向您展示如何为每个职位组以及整个团队强加每个组合的工资限制。这有帮助吗?您可能需要考虑将数据放在数据库中并使用Rails。

team_max_salary = 300
players = {player1: {position: :qb, salary: 15, rating: 9}, player2: {postion: :rb, salary: 6, rating: 6},...}
group_info = {qb: {nplayers: 2, max_salary: 50}, rb: {nplayers: 2, max_salary: 50}, ... } 

groups = group_info.keys
players_by_group = {}
groups.each {|g| players_by_group[g] = []}
players.each {|p| players_by_group[p.position] << p} 
combinations_for_team = []
groups.each do |g|
  combinations_by_group = players_by_group[g].combinations(group_info[g][:nplayers]).select {|c| salary(c) <= group_info[g][:max_salary]} 
  # Possibly employ other criteria here to further trim combinations_by_group
  combinations_for_team = combinations_for_team.product(combinations_by_group).flatten(1).select {|c| salary(c) <= team_max_salary}
end

我可能错过了flatten(1)。注意我已经制作了玩家键符号(例如:AaronRogers`),但你当然可以使用字符串。