给定一些约束,计算所有可能的组合

时间:2020-11-06 22:01:12

标签: algorithm performance combinatorics

我正在尝试解决个人项目中的算法问题。

  1. 在这场比赛中有8位玩家。
  2. 他们玩2vs2
  3. 同时进行两场比赛(以便所有8位玩家都在比赛)
  4. 每位球员必须与其他所有球员进行7场比赛(在同一支球队中)一次
  5. 比赛结束时,每位球员进行了7场比赛。
  6. 比赛共进行14场比赛
  7. 比赛的排列不会算作新比赛。 (即,比赛定义为一组比赛)

这是一个与A,B,C,D,E,F,G,H玩家进行的比赛示例:

Match  1 :   A, B  VS  C, D.                                Score = ____ : ____
Match  2 :   E, F  VS  H, G.                                Score = ____ : ____
Match  3 :   C, A  VS  B, D.                                Score = ____ : ____
Match  4 :   E, G  VS  H, F.                                Score = ____ : ____
Match  5 :   A, D  VS  C, B.                                Score = ____ : ____
Match  6 :   H, E  VS  F, G.                                Score = ____ : ____
Match  7 :   E, A  VS  B, F.                                Score = ____ : ____
Match  8 :   C, G  VS  H, D.                                Score = ____ : ____
Match  9 :   A, F  VS  E, B.                                Score = ____ : ____
Match 10 :   H, C  VS  D, G.                                Score = ____ : ____
Match 11 :   A, G  VS  H, B.                                Score = ____ : ____
Match 12 :   C, E  VS  D, F.                                Score = ____ : ____
Match 13 :   H, A  VS  B, G.                                Score = ____ : ____
Match 14 :   C, F  VS  E, D.                                Score = ____ : ____

请注意,Match iMatch i+1同时播放if i%2==1

问题:

可以进行多少个不同的比赛?这些比赛是哪些?

我尝试了一种蛮力的方法,但是它太慢了。有更好的算法吗?

编辑:

欢迎使用

代码。尤其是python

2 个答案:

答案 0 :(得分:1)

问题是高度对称的,让我以更紧凑的形式编写

AB CD + EF GH                              
AC BD + EG FH                              
AD BC + EH FG 
AE BF + CG DH
AF BE + CH DG
AG BH + CE DF
AH BG + CF DE

对于8个玩家,有28个可能的团队,由两个人组成(AB,AC,AD ...),并且全部都出现在表中,每个正好出现一次。 AB和BA是同一支球队,我会选择第一种形式,因为它是字母顺序。 AB CD和AB CD是相同的搭配,我会选择第一种形式。 AB CD + EF GH和EF GH + AB CD只是比赛的排列,我会选择第一种形式。考虑到所有这些,我们将问题减少到在该模式中填充21个单词

AB __ + __ __
AC __ + __ __
AD __ + __ __
AE __ + __ __
AF __ + __ __
AG __ + __ __
AH __ + __ __

这样,每一行包含所有8个字母,每个字母恰好一次。而且这很容易被强行强制执行,大约花了15秒钟来计算(不向控制台写入组合),结果是10034775

static bool SolutionIsOK(string[,] matchesTuples)
{
    for (int i = 1; i < 7; ++i)
    {
        for (int j = 0; j < i; ++j)
        {
            string a1 = matchesTuples[j, 2];
            string a2 = matchesTuples[i, 2];

            if (a1[0] > a2[0] || a1[0] == a2[0] && a1[1] > a2[1])
            {
                string b1 = matchesTuples[j, 3];
                string b2 = matchesTuples[i, 3];

                int check1 = (1 << (a1[0] - 'A')) |
                             (1 << (a1[1] - 'A')) |
                             (1 << (b1[0] - 'A')) |
                             (1 << (b1[1] - 'A'));
                int check2 = (1 << (a2[0] - 'A')) |
                             (1 << (a2[1] - 'A')) |
                             (1 << (b2[0] - 'A')) |
                             (1 << (b2[1] - 'A'));

                if (check1 == check2) { return false; }
            }
        }
    }
    return true;
}

static void WriteSolution(string[,] matchesTuples)
{
    for (int i = 0; i < 7; ++i)
    {
        Console.WriteLine(matchesTuples[i, 0] + " " + matchesTuples[i, 1] + " + "
            + matchesTuples[i, 2] + " " + matchesTuples[i, 3]);
    }
    Console.WriteLine("------------------------------");
}

static int counter = 0;

static void placeTeam(int level, string[] teams, string[,] matchesTuples, bool[,] presentPlayers)
{
    if (level == teams.Length)
    {
        if (!SolutionIsOK(matchesTuples)) { return; };
        WriteSolution(matchesTuples);
        counter++; // solution found
        return;
    }

    string team = teams[level++];
    for (int i = 0; i < 7; ++i)
    {
        if (presentPlayers[i, team[0] - 'A']
         || presentPlayers[i, team[1] - 'A'])
        {
            continue;
        }
        presentPlayers[i, team[0] - 'A'] = true;
        presentPlayers[i, team[1] - 'A'] = true;

        for (int j = 1; j < 4; ++j)
        {
            if (matchesTuples[i, j] != null) { continue; }
            if (j == 3 && (matchesTuples[i, 2] == null)) { continue; }
            matchesTuples[i, j] = team;

            placeTeam(level, teams, matchesTuples, presentPlayers);

            matchesTuples[i, j] = null;
        }

        presentPlayers[i, team[0] - 'A'] = false;
        presentPlayers[i, team[1] - 'A'] = false;
    }
}

static void Main(string[] args)
{
    string[,] matchesTuples = new string[7, 4]; // AE BF + CG DH 
    bool[,] presentPlayers = new bool[7, 8];  // ABCDEFGH
    string[] teams = new string[28]; // AB, AC, AD, ..., BC, BD, ..., GH

    int i = 0;
    for (char c1 = 'A'; c1 < 'H'; ++c1)
    {
        for (char c2 = c1; ++c2 <= 'H';)
        {
            teams[i] = c1.ToString() + c2;
            if (c1 == 'A')
            {
                matchesTuples[i, 0] = teams[i];
                presentPlayers[i, c1 - 'A'] = true;
                presentPlayers[i, c2 - 'A'] = true;
            }
            ++i;
        }
    }

    placeTeam(7, teams, matchesTuples, presentPlayers);

    Console.WriteLine("Found " + counter);
    Console.ReadKey();
}

唯一棘手的部分是SolutionIsOK函数。它解决了最后一个未解决的对称问题。这两种情况相等:

AC BD + EG FH
AD BC + EH FG


AC BD + EH FG
AD BC + EG FH

因为只对第二个匹配进行了排列。仅当那些匹配包含相同的4个人时,才能对第二个匹配进行排列。可以检测到这种情况,我们只能选择按字母顺序排序的情况。而这正是SolutionIsOK的作用。

答案 1 :(得分:0)

您可以检查constraint satisfaction problem。这是关于SAT求解器或SMT求解器的。

也许您的问题可以定义为CS问题。

可以解决CS问题的示例库:

我知道我给你的是库,而不是算法,但是也许不需要重新发明轮子。