如何在Ruby中对世界杯组表进行排序

时间:2014-05-29 00:17:55

标签: ruby-on-rails ruby algorithm sorting

我正在编写一种算法来根据匹配数据创建和排序世界杯组表。因此,给出以下匹配数据:

[
  { id: 1, home_team: "Honduras", away_team: "Chile", home_score: 0, away_score: 1 },
  { id: 2, home_team: "Spain", away_team: "Switzerland", home_score: 0, away_score: 1 },
  { id: 3, home_team: "Chile", away_team: "Switzerland", home_score: 1, away_score: 0 },
  { id: 4, home_team: "Spain", away_team: "Honduras", home_score: 2, away_score: 0 },
  { id: 5, home_team: "Chile", away_team: "Spain", home_score: 1, away_score: 2 },
  { id: 6, home_team: "Honduras", away_team: "Switzerland", home_score: 0, away_score: 0 }
]

我的程序会产生这个(订单很重要):

[{ goals_for: 4, goals_against: 2, goal_diff: 2,  points: 6, name: "Spain" },
 { goals_for: 3, goals_against: 2, goal_diff: 1,  points: 6, name: "Chile" },
 { goals_for: 1, goals_against: 1, goal_diff: 0,  points: 4, name: "Switzerland" },
 { goals_for: 0, goals_against: 3, goal_diff: -3, points: 1, name: "Honduras" }]

这很好,除非有双向或三向联系。然后标准变得复杂。这是按优先顺序排列的:

  1. 最多分数
  2. 最大的目标差异
  3. 最伟大的目标
    • 如果存在平局,则使用以下内容
  4. 绑定球队之间比赛的最高分数
  5. 绑定球队之间比赛的最大目标差异
  6. 最佳目标是从并列球队之间的比赛得分
  7. 抽签
  8. 问题

    我的排序功能满足前三个标准。如何在有双向或三向平局的情况下更改它?

      def sort
        teams.sort_by! do |team|
          [ team[:points], team[:goal_diff], team[:goals_for] ]
        end.reverse!
      end
    

    3-tie tie的例子

    [
      { id: 1, home_team: "Algeria", away_team: "Slovenia", home_score: 2, away_score: 1 },
      { id: 2, home_team: "USA", away_team: "Slovenia", home_score: 5, away_score: 1 },
      { id: 3, home_team: "England", away_team: "Slovenia", home_score: 4, away_score: 0 },
      { id: 4, home_team: "Algeria", away_team: "USA", home_score: 3, away_score: 0 },
      { id: 5, home_team: "USA", away_team: "England", home_score: 2, away_score: 0 },
      { id: 6, home_team: "England", away_team: "Algeria", home_score: 3, away_score: 2 }
    ]
    

    这个例子将根据标准1(分数)消除斯洛文尼亚。

    然后根据匹配数据的子集计算剩余三支球队的排名。该子集应仅包括绑定团队之间的匹配。在这种情况下,我们将使用包括阿尔及利亚,英格兰和美国在内的所有比赛重建表格。我们排除涉及斯洛文尼亚的比赛。

    表格应如下所示:

    | POS | TEAM        | GF | GA | GD | POINTS |
    | 1   | Algeria     | 5  | 3  |  2 | 3      |
    | 3   | England     | 3  | 4  | -1 | 3      |
    | 2   | USA         | 2  | 3  | -1 | 3      |
    

    阿尔及利亚队取得进球差距(标准5)。英格兰排名第二,因为goals for大于美国(标准6)。

    我的程序实际输出了这个,这是不正确的,因为它对关系没有任何作用,并且在标准3处停止。

    [ { goals_for: 7, goals_against: 4, goal_diff: 3, points: 6, name: "England" },
      { goals_for: 7, goals_against: 4, goal_diff: 3, points: 6, name: "Algeria" },
      { goals_for: 7, goals_against: 4, goal_diff: 3, points: 6, name: "USA" },
      { goals_for: 2, goals_against: 11, goal_diff: -9, points: 0, name: "Slovenia" }]
    

    这是完整的程序:

    class Calculator
      attr_reader :games, :teams
    
      def initialize(games)
        defaults = { goals_for: 0, goals_against: 0, goal_diff: 0, points: 0 }
        @games = games
        @teams = games.each_with_object([]) do |game, arr|
          arr.push({ name: game[:home_team] }.merge!(defaults))
          arr.push({ name: game[:away_team] }.merge!(defaults))
        end.uniq
      end
    
      def build_table
        build
        sort
        return teams
      end
    
    private
    
      def build
        games.each do |game|
          if game[:home_score].present? && game[:away_score].present?
            home_team = teams.detect { |team| team[:name] == game[:home_team] }
            away_team = teams.detect { |team| team[:name] == game[:away_team] }
    
            home_team[:goals_for]     += game[:home_score]
            home_team[:goals_against] += game[:away_score]
    
            away_team[:goals_for]     += game[:away_score]
            away_team[:goals_against] += game[:home_score]
    
            home_team[:goal_diff] = home_team[:goals_for] - home_team[:goals_against]
            away_team[:goal_diff] = away_team[:goals_for] - away_team[:goals_against]
    
            if game[:home_score] > game[:away_score]
              home_team[:points] += 3
            elsif game[:home_score] < game[:away_score]
              away_team[:points] += 3
            else
              home_team[:points] += 1
              away_team[:points] += 1
            end
          end
        end
      end
    
      def sort
        teams.sort_by! { |team| [ team[:points], team[:goal_diff], team[:goals_for] ] }.reverse!
      end
    end
    

1 个答案:

答案 0 :(得分:2)

您有一套明确定义的规则来确定如何订购团队。一种方法是编写一个一次一个地实现这些规则的排序例程,并在找到胜利者时进行短路:

def compare_points(a, b)
  a[:points] <=> b[:points]
end

def compare_goal_diff(a, b)
  a[:goal_diff] <=> b[:goal_diff]
end

def compare_teams(a, b)
  comparison = compare_points(a, b)
  return comparison unless comparison.zero?

  comparison = compare_goal_diff(a, b)
  return comparison unless comparison.zero?
  # Repeat for each type of comparison
  # ...
  comparison.zero? ? flip_coin : comparison
end

teams.sort! { |a, b| compare_teams(a, b) }.reverse!

比较单个值(如点)时,比较运算符<=>就足够了。对于更复杂的比较,您需要深入了解@games数组以确定获胜者,例如:

  def compare_points_from_matches_between(a, b)
    # Hand-waving follows
    # case
    # when team A has fewer points than team B in their meetings
    #   -1
    # when team B has fewer points than team A in their meetings
    #   1
    # else
    #   0
    # end
  end

根据您的规则应用每个比较。在每一步,如果比较为非零,则返回该值;否则你继续下一步。最后,如果比较仍为零,则可以翻转硬币。