一尘不染

生成体育联赛的自然时间表

algorithm

我正在寻找一种算法来为一组团队生成时间表。例如,设想一个运动季,每个团队互相比赛,一次作为主队,另一次作为客队在另一个团队场上。

要生成本赛季所有比赛的集合很容易,如果球队是球队列表,则可以执行以下操作:

set((x, y) for x in teams for y in teams if x != y)

但是我也想按时间顺序对游戏进行排序,以使其满足有效游戏时间表的约束,并且看起来也“自然随机”。

约束条件是游戏列表应可分组为多个回合,其中每个回合包含n / 2个游戏(其中n是团队数),其中每个团队与另一个团队配对。

为了使日程安排看起来更自然,两支球队不应在连续回合中两次面对对方。也就是说,如果(a,b)进行一轮比赛,则游戏(b,a)不应在下一局比赛。

另外,每支球队都应尽可能像客队一样进行每轮比赛,而其他球队则应像主队一样进行比赛。我认为不可能总是满足这个约束,所以拥有东西会更好。例如,一支球队不应玩8场主场比赛,然后再玩8场客场比赛。

下面是我现在得到的。该算法的主要问题是它经常卡在while循环中。特别是当团队数为16个或更多时。这也是非常低效的,因为它建立在使用随机样本函数并希望正确的基础上:

from random import sample
def season_schedule_order(teams, pairs):
    n_games_per_round = len(teams) // 2
    last_pairs = set()
    while pairs:
        r_pairs = set(sample(pairs, n_games_per_round))
        # Check that each team is present once in the round.
        r_teams = set(x for (x, y) in r_pairs) | set(y for (x, y) in r_pairs)
        if r_teams != teams:
            continue
        # Check that two teams doesn't face each other again.
        rev_pairs = set((y, x) for (x, y) in r_pairs)
        if rev_pairs & last_pairs:
            continue
        pairs -= r_pairs
        for p in r_pairs:
            yield p
        last_pairs = r_pairs

teams = set(['aik', 'djurgarden', 'elfsborg', 'gais',
             'gefle', 'hacken', 'halmstad', 'helsingborg'])
pairs = set((x, y) for x in teams for y in teams if x != y)
for (ht, at) in season_schedule_order(teams, pairs):
    print '%-20s %-20s' % (ht, at)

阅读 214

收藏
2020-07-28

共1个答案

一尘不染

我在这里找到一种方法
对此做了些微调整:

def round_robin(units, sets = None):
    """ Generates a schedule of "fair" pairings from a list of units """
    count = len(units)
    sets = sets or (count - 1)
    half = count / 2
    for turn in range(sets):
        left = units[:half]
        right = units[count - half - 1 + 1:][::-1]
        pairings = zip(left, right)
        if turn % 2 == 1:
            pairings = [(y, x) for (x, y) in pairings]
        units.insert(1, units.pop())
        yield pairings

teams = ['a', 'b', 'c', 'd']
print list(round_robin(teams, sets = len(teams) * 2 - 2))

现在,我只需要将其转换为plpgsql。:)

2020-07-28