Ruby组合数学

3
我希望生成一个足球比赛的赛程表,包含一系列俱乐部。每场比赛在周日进行,比赛开始时间随机选取,从match_starts_at数组中选择。每个俱乐部每个周日只能参加一场比赛。
例如:
假设有以下这些俱乐部:
Club Atlético All Boys
Asociación Atlética Argentinos Juniors
Arsenal Fútbol Club
Club Atlético Banfield
Club Atlético Belgrano
Club Atlético Boca Juniors
Club Atlético Colón
Club Estudiantes de La Plata
Club Deportivo Godoy Cruz Antonio Tomba
Asociación Mutual Social y Deportiva Atlético de Rafaela
Club Atlético Independiente
Club Atlético Lanús
Club Atlético Newell's Old Boys
Club Olimpo
Racing Club
Club Atlético San Martín
Club Atlético San Lorenzo de Almagro
Club Atlético Tigre
Club Atlético Unión
Club Atlético Vélez Sarsfield

结果应该类似于这里看到的内容:http://www.afa.org.ar/index.php?option=com_content&view=article&id=16780%3Afixture-del-torneo-de-primera-division&Itemid=100 俱乐部结构示例:
=> # Club @id=1 @name="Example Name"

=> # Club @id=2 @name="Example2 Name"

夹具结构示例:

=> # Fixture @id=1 @datetime='2011-11-19 19:12:49' @home_id=1 @away_id=2

一个 Fixture 对象需要以下内容才能保存到数据库:
a home club (:home)    
an away club (:away)    
and the time of the match (:datetime)

每个俱乐部只应与其他俱乐部比赛一次,所有俱乐部都应该在主场打一场比赛,另一场比赛则在客场进行,等等。每天应该有10场比赛。 我如何创建比赛列表?
这是我目前为止所做的。
  competition = Competition.get(1)
  clubs = Club.all #20 clubs
  @time = Time.now
  @count = 0
  until @time.sunday? do
     @time += (24*60*60) # add 1 day until it's sunday
  end
  @first_time = @time
  @fixture = {1 => []}
  clubs.combination(2).each_with_index do |(club1, club2), idx|
    Fixture.create(
      :home => idx.even? ? club1 : club2,
      :away => idx.even? ? club2 : club1,
      :datetime =>  available_fixture_date(club1,club2)
    ).save
  end

  def getFecha(club1, club2)
    @fixture.keys.each do |fecha|
      if (!@fixture[fecha].include?(club1.name) && !@fixture[fecha].include?(club2.name))
        @fixture[fecha] << club1.name
        @fixture[fecha] << club2.name
        @fixture[@fixture.keys.last + 1] = []
        return fecha
      end
    end
  end

  def available_fixture_date(club1, club2)
    fecha = getFecha(club1, club2)
    match_starts_at = ['16:00', '17:30', '18:10', '22:00']
    match_time = match_starts_at.shuffle.first
    @time  = @first_time + (24*60*60) * fecha * 7
    Time.new(@time.year, @time.month, @time.day, match_time[0,2], match_time[3,2])
 end

我的代码返回了超过19个日期,但每个日期应该只有10个匹配项。


你希望每场比赛之间的时间间隔是多久?我不确定我理解了11月20日、11月27日、11月20日的输出……它们是否应该像这样交替? - d11wtq
比赛可以在同一天同时开始。但是每个队每天只允许参加一场比赛。比赛在星期日进行。以下是阿根廷足球联赛的赛程安排示例:http://www.eldia.com.ar/publi/fixture2010.jpg - marcosdsanchez
在你的例子中,B队在同一天内比赛两次,相隔90分钟。我猜那是个笔误吧? :) - d11wtq
是的,我已经纠正了添加更多团队和真实世界示例的问题。 - marcosdsanchez
3个回答

2

由于需要查询现有数据以找出已经被占用的日期,因此您将无法像配对团队那样获得漂亮的一行代码。但这应该能很好地工作。请注意,我使用了ActiveSupport的时间辅助函数,但如果您没有ActiveSupport可用并且不想包含它,则可以使用类似Chronic的东西。

def available_fixture_date(club1, club2)
  last_played = (club1.fixtures | club2.fixtures).max(:datetime)
  last_played.nil? ? DateTime.now.sunday : last_played + 1.week
end

def create_fixtures(clubs)
  clubs.combination(2).each_with_index do |(club1, club2), idx|
    Fixture.create(
      :home     => idx.even? ? club1 : club2,
      :away     => idx.even? ? club2 : club1,
      :datetime =>  available_fixture_date(club1, club2)
    )
  end
end

club1.fixtures不存在。 - marcosdsanchez
我已经更新了我的帖子,其中包含我的当前代码,如果我有五个团队,它将无法运行。 - marcosdsanchez
这几乎是完美的,我基于此进行了工作。它只需要如何设置匹配的正确日期。 - marcosdsanchez

1

我认为你在这里寻找的一般算法是轮询算法。以下内容对我来说可以正确获取日期,最终得到19个日期总计,每天10场比赛:

DAY = 24 * 60 * 60
MATCH_START_TIMES = ['16:00', '17:30', '18:10', '22:00']

def fixture_date(fecha)
  # select a random start time
  match_time = MATCH_START_TIMES.sample

  @time = @first_time + DAY * fecha * 7
  Time.new(@time.year, @time.month, @time.day, match_time[0,2].to_i, match_time[3,2].to_i)
end

# uses round-robin indexing algorithm described on
# http://en.wikipedia.org/wiki/Round-robin%5Ftournament#Scheduling_algorithm
def round_robin(n, round)
  arr = [*0...n]
  arr.insert 1, *arr.pop(round)
  [arr.slice(0, n/2), arr.slice(n/2, n).reverse]
end

def find_club_combination(clubs, round, pair)
  indexes = round_robin(clubs.size, round)
  index_a, index_b = indexes.first[pair], indexes.last[pair]
  [clubs[index_a], clubs[index_b]]
end

competition = Competition.get(1)
clubs = Club.all #20 clubs
@time = Time.now
@count = 0
@time += DAY until @time.sunday?

@first_time = @time

num_rounds = clubs.size - 1
matches_per_day = clubs.size / 2
(0...num_rounds).collect do |round|
  matches_per_day.times do |pair|
    club1, club2 = find_club_combination(clubs, round, pair)
    Fixture.create(
      :home => round.even? ? club1 : club2,
      :away => round.even? ? club2 : club1,
      :datetime => fixture_date(round)
    ).save
  end
end

这太棒了,我真的感谢你提供的代码!唯一不如预期的是,第一个俱乐部只在主场比赛,而没有像应该的那样轮流在主场和客场比赛等。 - marcosdsanchez
我编辑了答案,将 pair.even? 更改为 round.even?,所以现在他们应该正确地交替主场/客场。 - Stuart M

1

不太确定您在寻找什么,但似乎您想要一种更简单的方式来进行时间计算?

如果是这样的话,Chronic 是相当不错的。

Chronic.parse('next sunday at 4pm')
#=> Sun Nov 20 16:00:00 -0800 2011

match_starts_at = ['16:00', '17:30', '18:10', '22:00']
Chronic.parse("sunday at #{match_starts_at[0]}")
#=> Sun Nov 20 16:00:00 -0800 2011

我编辑了问题,包括更多关于问题的信息,并添加了一个输出示例。 - marcosdsanchez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接