4人比赛日程安排,Python

4人比赛日程安排,Python,python,tournament,Python,Tournament,我正试图开发一种算法,为我的家庭每年举办的一次比赛制定时间表。我写了一个只部分有效的解决方案;它似乎适用于2^x玩家,但不适用于介于两者之间的玩家 Parcheesi是一场一次4人的比赛,不多也不少,因此我们安排比赛,以便在第一轮比赛中有4人(16、28、32等)的倍数,一次有n/4场比赛。然后在第二轮,每个人都被洗牌,扮演新人。在第三轮,同样的事情发生了。理想情况下,任何人都不会和其他人玩两次。这就是我的困境的症结所在,我试图对没有人再扮演其他人的属性进行编码 这是我的方法。我确信代码中存在效

我正试图开发一种算法,为我的家庭每年举办的一次比赛制定时间表。我写了一个只部分有效的解决方案;它似乎适用于2^x玩家,但不适用于介于两者之间的玩家

Parcheesi是一场一次4人的比赛,不多也不少,因此我们安排比赛,以便在第一轮比赛中有4人(16、28、32等)的倍数,一次有n/4场比赛。然后在第二轮,每个人都被洗牌,扮演新人。在第三轮,同样的事情发生了。理想情况下,任何人都不会和其他人玩两次。这就是我的困境的症结所在,我试图对没有人再扮演其他人的属性进行编码

这是我的方法。我确信代码中存在效率低下的问题,所以请随意提出建议(尽管我并不担心效率)。我只想让它在3个以上的回合和任何4人的倍数下工作

import numpy as np
import itertools
import sys

num_players = 32
players = np.arange(1,num_players+1)

num_games = 3
games = np.arange(1,num_games+1)
game_matchups = {}

matchups = {}
for player in players:
    matchups[player] = []

for game in games:
    tables = [ [] for i in range(int(num_players/4)) ]
    for player in players:
        for i,table in enumerate(tables):
            if player in list(itertools.chain(*tables)):
                break
            if len(table) == 0:
                table.append(player)
                break
            if len(table) == 4:
                continue             
            else:
                for j,opp in enumerate(table):
                    if player in matchups[opp]:
                        break
                    else:
                        if j == len(table)-1:
                            table.append(player)
                            break
                        else:
                            continue

    game_matchups[game] = tables           
    for table in tables:
        if len(table) != 4:
            sys.exit((str(num_players)+' players with '+str(num_games)+' games doesnt work!'))
        for i,p in enumerate(table):
            matchups[p] = matchups[p] + (table[:i]+table[i+1:])
    order = order*-1
如果玩家数量为32,我最多可以安排5轮比赛。但如果我增加到36名球员,它就会崩溃。在第二轮比赛中,它有点“用完”了桌子,而且它不能把33号玩家添加到一个他还没有和某人比赛过的桌子上

我尝试过向后、向前/向后、交替、随机化地遍历玩家列表,并将这些玩家放入表中,等等,但似乎没有任何效果


在实践中,我们已经手动制定了此计划,并且效果良好。我想写这个程序是对我自己的一个挑战,但是我被卡住了。

如果你想在不重新配对的情况下进行多轮比赛,你需要从16开始将你的人数乘以4

例如,如果第一张桌子上有玩家1、2、3、4(无论你如何组织其他桌子),你的第二轮将需要至少4张桌子(4名玩家各一张桌子),以确保这四名玩家不会坐在同一张桌子上。你需要16个人来填这四张桌子。这16个人应该允许你在不重新配对的情况下进行5轮比赛。考虑到玩家1、2、3和4再也不能见面,他们将在剩下的回合中各自独占一张桌子。在这一点上,他们每个人都有12个以上的对手,如果你把它们完美地混合在一起,那将是每轮3人,总共4轮(总共5轮)。因此,16个人最多只能打5轮

[EDIT2]我最初认为需要16的倍数,但后来发现我在布景操作中犯了一个错误。你可以为20个人打多轮。我在两个示例中都修复了它

下面是一个蛮力的方法,使用回溯找到一个四人组合,不会重新配对任何人。它使用集合来控制配对冲突,并使用itertools combinations()函数来生成四人组(4的组合)和配对(四人组中2的组合)

如果您希望进行的回合数超过允许的人数,您可能希望调整此选项以使用Counter()(来自集合)而不是set来实现每个玩家的“最大重新配对计数”

[编辑]这是函数的一个变体,具有最大配对参数和随机球员排列:

from itertools import combinations,chain
from collections import Counter
from random import shuffle

def arrangeTables(players, maxPair, alreadyPaired):
    tables        = players//4
    result        = [[]] * tables # list of foursomes
    tableNumber   = 0
    allPlayers    = set(range(1,players+1))

    def randomFoursomes():
        remainingPlayers = list(allPlayers - set(chain(*result[:tableNumber])))
        if maxPair > 1: shuffle(remainingPlayers)
        return combinations(remainingPlayers,4)

    foursomes     = [randomFoursomes()]
    allowedPairs  = 1
    while True:
        foursome = next(foursomes[tableNumber],None)
        if not foursome and allowedPairs < maxPair:
            foursomes[tableNumber] = randomFoursomes()
            allowedPairs += 1
            continue
        if not foursome:            
            tableNumber -= 1
            if tableNumber < 0: return None
            allowedPairs = 1
            foursomes.pop()
            continue
        foursome = sorted(foursome)
        if any(alreadyPaired[pair] >= allowedPairs for pair in combinations(foursome,2)):
            continue
        result[tableNumber] = foursome
        tableNumber   += 1
        if tableNumber == tables: break
        foursomes.append(randomFoursomes())
        allowedPairs   = 1
    return result

def tournamentTables(players, maxPair=1):
    rounds  = []    # list of foursome for each round (one foresome per table)
    paired  = Counter() # of player-player tuples (lowest payer number first)
    while True:
        roundTables = arrangeTables(players,maxPair,paired)
        if not roundTables: break
        shuffle(roundTables)
        rounds.append(roundTables)
        for foursome in roundTables:
            pairs  = combinations(foursome,2)
            paired = paired + Counter(pairs)
    return rounds
请注意,您仍然可以最多使用1次,以不允许重新配对(即每个玩家组合1次配对):

[EDIT3]优化版本

我对该函数进行了更多的实验,并添加了一些优化。它现在可以在合理的时间内完成36人组合。正如我所怀疑的,大部分时间都花在试图(但失败)找到第六轮解决方案上。这意味着,如果您在5轮后立即退出该功能,您将始终获得快速响应

更进一步说,我发现,超过32人,一些球员的计数需要更长的时间。在找到可能的回合后,他们会浪费额外的时间来确定不再有可能的回合(例如36人5轮)。因此,36、40和44名玩家需要更长的时间,但48名玩家收敛到5轮解决方案的速度要快得多。数学家可能对这种现象有一个解释,但在这一点上我无法理解

目前,我发现当您有64人或更多人时,该函数只能产生5轮以上的结果。(因此将其停在5似乎是合理的)

以下是优化后的函数:

def arrangeTables(players, tables, alreadyPaired):
    result        = [[]] * tables # list of foursomes
    tableNumber   = 0
    threesomes    = [combinations(range(2,players+1),3)] 
    firstPlayer   = 1     # first player at table (needs 3 opponents)
    placed        = set() # players sitting at tables so far (in result)
    while True:
        opponents = next(threesomes[tableNumber],None)
        if not opponents:
            tableNumber -= 1
            threesomes.pop()
            if tableNumber < 0: return None
            placed.difference_update(result[tableNumber])
            firstPlayer = result[tableNumber][0] 
            continue
        foursome    = [firstPlayer] + list(opponents)
        pairs       = combinations(foursome,2)
        if not alreadyPaired.isdisjoint(pairs): continue
        result[tableNumber] = foursome
        placed.update(foursome)
        tableNumber += 1
        if tableNumber == tables: break
        remainingPlayers = [ p for p in range(1,players+1) if p not in placed ]
        firstPlayer = remainingPlayers[0]
        remainingPlayers = [ p for p in remainingPlayers[1:] if (firstPlayer,p) not in alreadyPaired ]
        threesomes.append(combinations(remainingPlayers,3))       
    return result

def tournamentTables(players):
    tables  = players//4
    rounds  = []    # list of foursome for each round (one foresome per table)
    paired  = set() # player-player tuples (lowest payer number first)
    while True: # len(rounds) < 5
        roundTables = arrangeTables(players,tables,paired)
        if not roundTables: break
        rounds.append(roundTables)
        for foursome in roundTables:
            paired.update(combinations(foursome,2))
    return rounds
def排列表(播放器、表格、已播放):
结果=[[]]*表格#四人列表
tableNumber=0
三人组=[组合(范围(2,玩家+1),3)]
firstPlayer=1#桌上第一名玩家(需要3名对手)
placed=set()#到目前为止坐在桌子旁的玩家(结果)
尽管如此:
对手=下一个(三人[表号],无)
如果不是反对者:
表号-=1
三人组
如果tableNumber<0:返回None
放置。差异\u更新(结果[表编号])
firstPlayer=结果[tableNumber][0]
持续
四人组=[第一名玩家]+名单(对手)
成对=组合(四人组,2)
如果尚未准备就绪。isdisjoint(对):继续
结果[表格编号]=四人组
放置。更新(四人制)
表号+=1
如果tableNumber==表:中断
剩余玩家=[p代表范围内的p(1,玩家+1),如果p未就位]
firstPlayer=剩余玩家[0]
RemainingPlayer=[p代表RemainingPlayer中的p[1:]如果(firstPlayer,p)不在alreadyPaired中]
三人组。附加(组合(剩余玩家,3))
返回结果
def锦标赛表(玩家):
表=玩家//4
轮数=[]#每轮四人名单(每桌一人)
paired=set()#player-player元组(首先是最低付款人编号)
正确时:#len(轮数)<5
圆桌会议=安排表(球员、桌子、配对)
如果不是圆桌会议:休息
from itertools import combinations,chain
from collections import Counter
from random import shuffle

def arrangeTables(players, maxPair, alreadyPaired):
    tables        = players//4
    result        = [[]] * tables # list of foursomes
    tableNumber   = 0
    allPlayers    = set(range(1,players+1))

    def randomFoursomes():
        remainingPlayers = list(allPlayers - set(chain(*result[:tableNumber])))
        if maxPair > 1: shuffle(remainingPlayers)
        return combinations(remainingPlayers,4)

    foursomes     = [randomFoursomes()]
    allowedPairs  = 1
    while True:
        foursome = next(foursomes[tableNumber],None)
        if not foursome and allowedPairs < maxPair:
            foursomes[tableNumber] = randomFoursomes()
            allowedPairs += 1
            continue
        if not foursome:            
            tableNumber -= 1
            if tableNumber < 0: return None
            allowedPairs = 1
            foursomes.pop()
            continue
        foursome = sorted(foursome)
        if any(alreadyPaired[pair] >= allowedPairs for pair in combinations(foursome,2)):
            continue
        result[tableNumber] = foursome
        tableNumber   += 1
        if tableNumber == tables: break
        foursomes.append(randomFoursomes())
        allowedPairs   = 1
    return result

def tournamentTables(players, maxPair=1):
    rounds  = []    # list of foursome for each round (one foresome per table)
    paired  = Counter() # of player-player tuples (lowest payer number first)
    while True:
        roundTables = arrangeTables(players,maxPair,paired)
        if not roundTables: break
        shuffle(roundTables)
        rounds.append(roundTables)
        for foursome in roundTables:
            pairs  = combinations(foursome,2)
            paired = paired + Counter(pairs)
    return rounds
for roundNumber,roundTables in enumerate(tournamentTables(12,2)):
    print(roundNumber+1,roundTables)

1 [[3, 6, 8, 10], [1, 2, 5, 7], [4, 9, 11, 12]]
2 [[1, 4, 5, 11], [3, 6, 7, 8], [2, 9, 10, 12]]
3 [[1, 4, 8, 9], [5, 6, 7, 12], [2, 3, 10, 11]]
for roundNumber,roundTables in enumerate(tournamentTables(20)):
    print(roundNumber+1,roundTables)

1 [[1, 2, 3, 4], [13, 14, 15, 16], [17, 18, 19, 20], [9, 10, 11, 12], [5, 6, 7, 8]]
2 [[3, 7, 14, 18], [4, 11, 15, 19], [1, 5, 9, 13], [2, 6, 10, 17], [8, 12, 16, 20]]
3 [[2, 5, 12, 18], [1, 6, 11, 14], [4, 9, 16, 17], [3, 8, 13, 19], [7, 10, 15, 20]]
def arrangeTables(players, tables, alreadyPaired):
    result        = [[]] * tables # list of foursomes
    tableNumber   = 0
    threesomes    = [combinations(range(2,players+1),3)] 
    firstPlayer   = 1     # first player at table (needs 3 opponents)
    placed        = set() # players sitting at tables so far (in result)
    while True:
        opponents = next(threesomes[tableNumber],None)
        if not opponents:
            tableNumber -= 1
            threesomes.pop()
            if tableNumber < 0: return None
            placed.difference_update(result[tableNumber])
            firstPlayer = result[tableNumber][0] 
            continue
        foursome    = [firstPlayer] + list(opponents)
        pairs       = combinations(foursome,2)
        if not alreadyPaired.isdisjoint(pairs): continue
        result[tableNumber] = foursome
        placed.update(foursome)
        tableNumber += 1
        if tableNumber == tables: break
        remainingPlayers = [ p for p in range(1,players+1) if p not in placed ]
        firstPlayer = remainingPlayers[0]
        remainingPlayers = [ p for p in remainingPlayers[1:] if (firstPlayer,p) not in alreadyPaired ]
        threesomes.append(combinations(remainingPlayers,3))       
    return result

def tournamentTables(players):
    tables  = players//4
    rounds  = []    # list of foursome for each round (one foresome per table)
    paired  = set() # player-player tuples (lowest payer number first)
    while True: # len(rounds) < 5
        roundTables = arrangeTables(players,tables,paired)
        if not roundTables: break
        rounds.append(roundTables)
        for foursome in roundTables:
            paired.update(combinations(foursome,2))
    return rounds