我正在寻找一种算法来为一组团队生成时间表。例如,设想一个运动季,每个团队互相比赛,一次作为主队,另一次作为客队在另一个团队场上。
要生成本赛季所有比赛的集合很容易,如果球队是球队列表,则可以执行以下操作:
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)
我在这里找到一种方法, 对此做了些微调整:
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。:)