如何从8位选手创建7轮独特的选手组合

时间:2018-11-03 21:44:00

标签: php c++ c algorithm

我遇到了问题,正在尝试开发或使用算法来解决此问题。

我有8个玩家,每个玩家可以与其他玩家组成7个独特的团队。一队由2名球员组成。

这里是一个例子, 可以说所有玩家的名字都是

[A,B,C,D,E,F,G,H]

现在,从这8名球员中,我可以创建总共28个独特的团队组合。

现在我要进行7轮比赛,每轮比赛他们都必须与独特的球队一起比赛。

我尝试了round robin algorithm,但是它可以工作4个回合。无法创建7个回合。

A B C D
E F G H 

ROUND 1: AE, BF, CG, DH 

E A B C
F G H D

ROUND 2: EF, AG, BH, CD 

F E A B 
G H D C 

ROUND 3: FG, EH, AD, BC, 

G F E A 
H D C B

ROUND 4: GH, FD, EC, AB

我仍然需要使用一组独特的球员组合再进行3回合。

任何帮助将不胜感激。谢谢。

1 个答案:

答案 0 :(得分:1)

为保持最大多样性,切勿将每个玩家与之前配对过的玩家配对。由于有8个玩家和7个回合,这意味着每个玩家将需要与对方完全匹配一次。每轮有4对。

由于有8个玩家,我们可以将每个玩家表示为一个字节。

Enum PlayerId : Byte {
    PLAYER_A = 1,
    PLAYER_B = 2,
    PLAYER_C = 4,
    PLAYER_D = 8,
    PLAYER_E = 16,
    PLAYER_F = 32,
    PLAYER_G = 64,
    PLAYER_H = 128
}

// player index to player id lookup
PlayerId _playerIds[] = { PLAYER_A, PLAYER_B, PLAYER_C, ... }

对于每个单独的玩家,有7个独特的配对,所有配对都将在游戏中显示。对于每个回合,我们需要4个这样的配对而没有冲突:即,所有玩家在所有此类配对中必须恰好一次。

我们将使用图形来表示所有可能性。我们将为每个可能的配对创建一个节点,并在彼此兼容的节点之间创建边缘(即,玩家选择中没有冲突)。

public class PairingGraph {
    Map<Byte, PairingNode*> nodes = new Map<Byte, PairingNode*>();        
    Map<Byte, PairingEdge*> edges = new Map<Byte, PairingEdge*>();

    PairingNode* AddPairing(Player p1, Player p2) {
        PairingNode n(this, p1, p2);

        if(nodes.ContainsKey(n->id)) {
            throw new Exception("Duplicate Player Pairing");

        nodes[n->id] = n;

        for other : nodes {
            if(n->id & other->id) continue;                
            edges->Add(new PairingEdge(this, n, other)); 
        }

        return n;
    }

    boolean RemoveNode(Byte id) {
        if (!nodes.ContainsKey(id)) return false;

        auto n = nodes[id];

        // first remove any edges that this node is part of.
        for e : n->edges {
            if (e->p1 != n) {
                e->p1->edges.remove(e);
            } else {
                e->p2->edges.remove(e);
            }

            delete e;
        }

        nodes.remove(n);
        delete n;
        return true;
    }

    PairingGraph() {
        // initialize the graph with all player combinations
        for(int i = 0; i < 8; i++) {
            for(int j = i + 1; j < 8; j++) {
                AddPairing(_playerIds[i], _playerIds[j]);
            }
        }            
    }
}

public class PairingNode {
    PairingGraph* graph;
    List<PairingEdge> edges;
    PlayerId p1;
    PlayerId p2;
    Byte id;

    PairingNode(PairingGraph* g, PlayerId p1, PlayerId p2) {
        if(p1 == p2) throw new Exception("Two distinct players are required to create a PairingNode");
        this.graph = g;
        this.p1 = p1;
        this.p2 = p2;
        this.id = p1 | p2;
    }
}

public class PairingEdge {
    PairingGraph* g;
    PairingNode* p1;
    PairingNode* p2;
    Byte id;

    PairingEdge(PairingGraph* g, PairingNode* p1, PairingNode* p2) {
        if(p1->id & p2->id) throw new Exception("Cannot create edges between PairingNode's that share a player");
        this.graph = g;
        this.p1 = p1;
        this.p2 = p2;
        this.id = p1->id | p2->id;

        this.p1.edges.Add(this);
        this.p2.edges.Add(this);
    }
}

现在我们可以使用所有可能的组合构造一个PairingGraph,我们用于创建配对的算法很简单:从图中随机选择和删除节点,并附加了一个约束,即删除的节点不会与该回合中以前选择的任何节点冲突

List<Tuple<PlayerId, PlayerId>>* SelectPlayersForRound(PairingGraph* g) {

    List<Tuple<PlayerId, PlayerId>> results = new List<Tuple<PlayerId, PlayerId>>;
    Byte restrictions = 0;

    // start by randomly selecting an intial node.
    int selection = rand(0, g->nodes->count -1);
    auto last_node = g->nodes[g->nodes->keys(selection)];
    restrictions |= n->id;
    selectedNodes.Add(n);

    // select the three other player pairs, informed from previous selections.
    for(int i = 1; i < 4; i++)
    {            
        // start by randomly selecting an edge from the last node
        // it necessarily won't conflict with the last_node, but may
        // not meet all restriction criteria
        int selection = rand(0, last_node->edges->count -1);

        // Grab the node on the opposite end of the selected edge
        auto e = last_node->edges[selection];
        auto next_node = (e->p1 == last_node)? e->p2 : e->p1;

        // If the node doesn't meet our current restrictions, try again.
        while(next_node->id & restrictions) {
            selection++;
            if(selection == last_node->edges->count) selection++;
            e = last_node->edges[selection];
            next_node = (e->p1 == last_node)? e->p2 : e->p1;
        }

        // Remove the last_node from the Graph
        g->RemoveNode(last_node->id);

        // update for next iteration
        last_node = curr_node;
        restrictions |= last_node->id;
    }

    // Remove the last_node from the Graph
    g->RemoveNode(last_node->id);

    return results;
}

只需为每个游戏创建一个新的PairingGraph,然后在每个回合中调用SelectPlayersForRound,就可以完成!

请注意,这只是一些伪代码(因为您未指定语言),并且未经测试。希望对您有所帮助:)

编辑:您已在帖子中添加了一些语言。我的伪代码或多或少是c ++,因此您应该可以轻松地出于自己的目的进行复制-修改。

EDIT2:改进了选择例程,以利用last_node的兼容边列表(减少潜在冲突的次数)

EDIT3:修复了一些条件逻辑,其中我不正确地使用^而不是&