Python 将阵列拆分为子阵列5次,同时在所有子阵列中保持唯一对

Python 将阵列拆分为子阵列5次,同时在所有子阵列中保持唯一对,python,algorithm,Python,Algorithm,背景:我是一名会议经理,在会议期间,我需要将参与者分成几组。我想使用python,但我正在努力 我需要把70个人分成10组,每组7人。 另外,我需要将他们分开5次(5轮)。 在每一轮比赛中,我都需要这一点,在这之前的每一轮比赛中,不会有一对搭档在同一个队中 假设人员是一个数组[0,1,…,69],将其拆分为多个组实际上是在创建大小相等的子数组7 一个好的输出应该是(例如,2个完全唯一的回合和缺少3个回合): 需要帮忙吗? 我原以为这在python中是一项简单的任务,但我现在几个小时都无法正确完成

背景:我是一名会议经理,在会议期间,我需要将参与者分成几组。我想使用python,但我正在努力

我需要把70个人分成10组,每组7人。 另外,我需要将他们分开5次(5轮)。 在每一轮比赛中,我都需要这一点,在这之前的每一轮比赛中,不会有一对搭档在同一个队中

假设人员是一个数组
[0,1,…,69]
,将其拆分为多个组实际上是在创建大小相等的子数组7

一个好的输出应该是(例如,2个完全唯一的回合和缺少3个回合):

需要帮忙吗? 我原以为这在python中是一项简单的任务,但我现在几个小时都无法正确完成。我会欣赏python代码,但任何其他我可以轻松编译和运行的语言也很棒


非常感谢。

顺便说一句,这个问题有很多不同的名字,一个是“社交高尔夫问题”。最初的问题发表在1850年的一些深奥的期刊上,被称为“柯克兰的女学生问题”。组合学是一个有趣的问题领域

我已经为这个有趣的问题找到了更多的解决方案,并且回来更新了这篇文章。下面的代码是进行分组的主要代码段

参与者对象各自维护一个“熟人”列表,该列表有助于确定他们是否可以加入一个团体,以及其他事项。这个程序可以在任何地方解决这个问题,从瞬间到一两分钟。平均来说,这不到5秒(我有一个旧的系统)。在Py3上运行它要快得多。不像遗传算法那么优雅,但更具确定性(也许?)

这段代码为一轮创建分组。每次会议尝试调用它5次(或参数指定的任何次数)

def run_round(round_id, participants, groups):
    RAND_CANDIDATE_BIAS  = 4
    num_regroup_attempts = len(participants) * 2
    num_rand_candidates  = groups[0].size

    for participant in participants:
        found = False
        random.shuffle(groups)

        # First attempt to find a group.
        for group in groups:
            if try_join(participant, group):
                found = True
                break

        # If not successful, see if getting other participants (regroup 
        # candidates) to change groups will create an opening for the 
        # participant.
        if not found:
            for _ in range(num_regroup_attempts):
                if not random.randint(0, RAND_CANDIDATE_BIAS):
                    # Grouped acquaintances will try to regroup.
                    acqs       = participant.acquaintances
                    candidates = [a for a in acqs if a.group]
                else:
                    # Randomly selected participants will try to regroup.
                    candidates = random.choices(participants,
                                                k=num_rand_candidates)
                    candidates = [cand for cand in candidates if cand.group]

                random.shuffle(candidates)

                for regroup_candidate in candidates:
                    if change_group(regroup_candidate, groups):

                        # Candidate regrouped, see if there's an opening now.
                        if try_join_any(participant, groups):
                            found = True
                            break
                if found:
                    break

    return mixer.Round(round_id, groups)
下面是程序的输出。每行的第一个数字是组号,可以忽略

Oo>>>>>> Sun Mar  8 20:53:12 2020 <<<<<<oO

Iteration 1 best so far: 1 groupless

[[[ Iteration 2 -- SOLVED!! ]]]

Conference(3,
    Round(1, [
        Group( 1, [ 1, 35, 41, 42, 44, 45, 57]),
        Group( 2, [ 2, 10, 37, 40, 43, 47, 55]),
        Group( 3, [ 3, 15, 31, 36, 62, 63, 68]),
        Group( 4, [ 4,  8, 20, 26, 29, 32, 54]),
        Group( 5, [ 5, 46, 64, 65, 66, 67, 69]),
        Group( 6, [ 6, 17, 22, 23, 27, 39, 53]),
        Group( 7, [ 7, 14, 21, 33, 48, 52, 60]),
        Group( 8, [ 9, 12, 25, 38, 49, 50, 70]),
        Group( 9, [11, 18, 19, 24, 34, 51, 59]),
        Group(10, [13, 16, 28, 30, 56, 58, 61]),
    ]),
    Round(2, [
        Group( 1, [ 1,  5, 10, 15, 51, 52, 70]),
        Group( 2, [ 2, 11, 25, 44, 46, 61, 68]),
        Group( 3, [ 3,  8, 12, 27, 34, 43, 67]),
        Group( 4, [ 4, 39, 41, 48, 55, 58, 65]),
        Group( 5, [ 6, 26, 28, 50, 59, 63, 64]),
        Group( 6, [ 7, 23, 36, 38, 45, 56, 66]),
        Group( 7, [ 9, 14, 16, 20, 22, 37, 42]),
        Group( 8, [13, 18, 33, 35, 54, 62, 69]),
        Group( 9, [17, 21, 24, 29, 31, 47, 49]),
        Group(10, [19, 30, 32, 40, 53, 57, 60]),
    ]),
    Round(3, [
        Group( 1, [ 1, 18, 27, 32, 37, 56, 68]),
        Group( 2, [ 2, 30, 45, 48, 50, 51, 67]),
        Group( 3, [ 3,  5,  6, 29, 38, 44, 60]),
        Group( 4, [ 4,  9, 23, 24, 40, 46, 62]),
        Group( 5, [ 7, 13, 22, 49, 55, 57, 63]),
        Group( 6, [ 8, 10, 25, 31, 33, 41, 59]),
        Group( 7, [11, 12, 15, 20, 35, 58, 66]),
        Group( 8, [14, 17, 19, 54, 61, 64, 70]),
        Group( 9, [16, 21, 26, 36, 43, 53, 65]),
        Group(10, [28, 34, 39, 42, 47, 52, 69]),
    ]),
    Round(4, [
        Group( 1, [ 1, 14, 31, 34, 46, 50, 58]),
        Group( 2, [ 2,  4,  6, 18, 21, 57, 66]),
        Group( 3, [ 3, 23, 26, 51, 55, 61, 69]),
        Group( 4, [ 5,  9, 13, 17, 32, 36, 59]),
        Group( 5, [ 7, 15, 37, 41, 53, 54, 67]),
        Group( 6, [ 8, 11, 40, 42, 56, 65, 70]),
        Group( 7, [10, 20, 27, 28, 49, 60, 62]),
        Group( 8, [12, 24, 30, 33, 39, 44, 63]),
        Group( 9, [16, 19, 35, 38, 47, 48, 68]),
        Group(10, [22, 25, 29, 43, 45, 52, 64]),
    ]),
    Round(5, [
        Group( 1, [ 1, 39, 43, 59, 61, 62, 66]),
        Group( 2, [ 2, 24, 38, 42, 53, 58, 64]),
        Group( 3, [ 3, 11, 16, 32, 33, 45, 49]),
        Group( 4, [ 4, 22, 31, 35, 60, 67, 70]),
        Group( 5, [ 5, 18, 20, 23, 41, 47, 63]),
        Group( 6, [ 6, 30, 36, 46, 52, 54, 55]),
        Group( 7, [ 7, 12, 17, 28, 51, 65, 68]),
        Group( 8, [ 8, 19, 21, 37, 44, 50, 69]),
        Group( 9, [ 9, 10, 29, 34, 48, 56, 57]),
        Group(10, [13, 14, 15, 25, 26, 27, 40]),
    ]))

Oo>>>>>> Sun Mar  8 20:53:13 2020 <<<<<<oO

Oo>>>>>>>太阳3月8日20:53:12 2020太阳3月8日20:53:13 2020>>>>>>>>周一3月9日02:13:33 2020周一3月9日02:13:38 2020以下是答案:

Round 1
  [ 0,11,18,50,56,60,63]
  [ 1,19,38,41,43,47,69]
  [ 2,27,37,55,57,65,66]
  [ 3,15,23,35,44,53,62]
  [ 4,21,28,34,49,61,67]
  [ 5,16,22,29,31,39,45]
  [ 6,10,14,20,24,25,46]
  [ 7,17,32,42,54,58,59]
  [ 8,12,13,26,30,36,52]
  [ 9,33,40,48,51,64,68]
Round 2
  [ 0,27,28,32,38,44,51]
  [ 1, 3,14,37,48,59,67]
  [ 2,24,34,45,47,50,68]
  [ 4,20,31,42,43,52,65]
  [ 5, 7,13,18,25,40,61]
  [ 6, 8,16,33,35,58,69]
  [ 9,22,26,41,53,60,66]
  [10,15,21,30,54,57,63]
  [11,17,19,23,36,39,64]
  [12,29,46,49,55,56,62]
Round 3
  [ 0, 8, 9,39,47,54,55]
  [ 1, 5,21,26,42,62,64]
  [ 2,10,11,33,44,52,67]
  [ 3,12,24,61,63,65,69]
  [ 4,19,22,25,51,56,57]
  [ 6,23,31,40,50,59,66]
  [ 7,16,28,37,46,53,68]
  [13,20,29,32,34,35,41]
  [14,17,18,27,30,43,45]
  [15,36,38,48,49,58,60]
Round 4
  [ 0,12,23,25,37,41,42]
  [ 1, 6,18,22,36,65,68]
  [ 2, 4,29,30,59,60,64]
  [ 3, 8,11,20,21,45,51]
  [ 5, 9,34,46,52,57,69]
  [ 7,15,19,24,26,27,31]
  [10,13,28,47,58,62,66]
  [14,32,33,39,49,50,53]
  [16,43,44,48,54,56,61]
  [17,35,38,40,55,63,67]
Round 5
  [ 0, 2, 3,13,22,43,46]
  [ 1,11,12,16,27,34,40]
  [ 4, 7,10,36,41,45,55]
  [ 5, 6,37,47,49,51,63]
  [ 8,15,25,32,64,66,67]
  [ 9,14,21,23,56,58,65]
  [17,31,33,57,60,61,62]
  [18,24,29,38,52,53,54]
  [19,28,30,35,42,48,50]
  [20,26,39,44,59,68,69]
这是我用作遗传算法适应度函数的C代码

#define ROUNDS 5
#define GROUPS 10
#define PEOPLE 7
#define POPULATION (GROUPS * PEOPLE)

char pairs[POPULATION][POPULATION];
int choices[ROUNDS][GROUPS][PEOPLE];

int computeScore(void)
{
    memset(pairs, 0, sizeof(pairs));
    for (int round = 0; round < ROUNDS; round++)
        for (int group = 0; group < GROUPS; group++)
            for (int a = 0; a < (PEOPLE-1); a++)
                for (int b = a+1; b < PEOPLE; b++)
                {
                    int personA = choices[round][group][a];
                    int personB = choices[round][group][b];
                    pairs[personA][personB]++;
                    pairs[personB][personA]++;
                }

    int score = 0;
    for (int a = 0; a < (POPULATION-1); a++)
        for (int b = a+1; b < POPULATION; b++)
            if (pairs[a][b] == 1)
                score++;
    return score;
}

您需要搜索配对/调度算法;我已更改了你的标签,以从该组获得帮助。“Python中的easy task”首先假定这是一个“easy task”——事实并非如此。你有2415对配对,分成10个
小团体,每5轮21对。你要求小团体在5轮中相互排斥且详尽无遗;我试图说服自己这是否可能。每一轮给你105对,不能在以后的任何一轮中重复;你确定你能连续地构建10个名单,其中包括7个从未一起工作过的人吗?这需要一组投影平面O(70,7),其“侧面要求”是在平面之间具有互斥且详尽的线条。可能第一轮为跨距=1,第二轮为跨距=7,第三轮为跨距=15,第四轮为跨距=31,等等。我无法表达我多么感谢您的帮助!非常感谢。另外,因为你提到了这个问题的名字,我觉得这个很好,以备将来参考和简单使用。我提出的代码比“近似”解算器更好=)它实际上以某种一致性解决了这些问题。让它运行最多一分钟,它会相当快地吐出分组。非常鼓舞人心。伟大的工作@user3386109验证了您的输出。你的代码需要多长时间才能得到答案?@Todd我一次运行遗传算法10分钟。跑了几圈才走运。遗传算法的问题是,它非常善于接近答案,但容易陷入局部极大值。第一代(随机)解决方案通常在700年代得分。这将在几秒钟内提高到1040,但从1040到1050需要很多时间,也需要一些运气。OTOH,为你的解决方案填写第三轮非常快。只有100万个可能的答案,并且花了大约20秒来尝试所有答案。100万份工作中有5份。我敢打赌,如果我们将这两种策略结合起来,我们可能会很快找到这个问题的答案集(或大部分答案)@user3386109@Todd没错,在您的代码生成4个有效回合后,我的代码可以找到第五个。我仍然不明白的是,为什么你的代码在第三轮失败了,却可以生成第四轮和第五轮。似乎应该是第5轮失败了,因为最后一轮应该是最受限制的。
Round 1
  [ 0,11,18,50,56,60,63]
  [ 1,19,38,41,43,47,69]
  [ 2,27,37,55,57,65,66]
  [ 3,15,23,35,44,53,62]
  [ 4,21,28,34,49,61,67]
  [ 5,16,22,29,31,39,45]
  [ 6,10,14,20,24,25,46]
  [ 7,17,32,42,54,58,59]
  [ 8,12,13,26,30,36,52]
  [ 9,33,40,48,51,64,68]
Round 2
  [ 0,27,28,32,38,44,51]
  [ 1, 3,14,37,48,59,67]
  [ 2,24,34,45,47,50,68]
  [ 4,20,31,42,43,52,65]
  [ 5, 7,13,18,25,40,61]
  [ 6, 8,16,33,35,58,69]
  [ 9,22,26,41,53,60,66]
  [10,15,21,30,54,57,63]
  [11,17,19,23,36,39,64]
  [12,29,46,49,55,56,62]
Round 3
  [ 0, 8, 9,39,47,54,55]
  [ 1, 5,21,26,42,62,64]
  [ 2,10,11,33,44,52,67]
  [ 3,12,24,61,63,65,69]
  [ 4,19,22,25,51,56,57]
  [ 6,23,31,40,50,59,66]
  [ 7,16,28,37,46,53,68]
  [13,20,29,32,34,35,41]
  [14,17,18,27,30,43,45]
  [15,36,38,48,49,58,60]
Round 4
  [ 0,12,23,25,37,41,42]
  [ 1, 6,18,22,36,65,68]
  [ 2, 4,29,30,59,60,64]
  [ 3, 8,11,20,21,45,51]
  [ 5, 9,34,46,52,57,69]
  [ 7,15,19,24,26,27,31]
  [10,13,28,47,58,62,66]
  [14,32,33,39,49,50,53]
  [16,43,44,48,54,56,61]
  [17,35,38,40,55,63,67]
Round 5
  [ 0, 2, 3,13,22,43,46]
  [ 1,11,12,16,27,34,40]
  [ 4, 7,10,36,41,45,55]
  [ 5, 6,37,47,49,51,63]
  [ 8,15,25,32,64,66,67]
  [ 9,14,21,23,56,58,65]
  [17,31,33,57,60,61,62]
  [18,24,29,38,52,53,54]
  [19,28,30,35,42,48,50]
  [20,26,39,44,59,68,69]
#define ROUNDS 5
#define GROUPS 10
#define PEOPLE 7
#define POPULATION (GROUPS * PEOPLE)

char pairs[POPULATION][POPULATION];
int choices[ROUNDS][GROUPS][PEOPLE];

int computeScore(void)
{
    memset(pairs, 0, sizeof(pairs));
    for (int round = 0; round < ROUNDS; round++)
        for (int group = 0; group < GROUPS; group++)
            for (int a = 0; a < (PEOPLE-1); a++)
                for (int b = a+1; b < PEOPLE; b++)
                {
                    int personA = choices[round][group][a];
                    int personB = choices[round][group][b];
                    pairs[personA][personB]++;
                    pairs[personB][personA]++;
                }

    int score = 0;
    for (int a = 0; a < (POPULATION-1); a++)
        for (int b = a+1; b < POPULATION; b++)
            if (pairs[a][b] == 1)
                score++;
    return score;
}
Round 1
  [ 0,10,20,30,40,50,60]
  [ 1,11,21,31,41,51,61]
  [ 2,12,22,32,42,52,62]
  [ 3,13,23,33,43,53,63]
  [ 4,14,24,34,44,54,64]
  [ 5,15,25,35,45,55,65]
  [ 6,16,26,36,46,56,66]
  [ 7,17,27,37,47,57,67]
  [ 8,18,28,38,48,58,68]
  [ 9,19,29,39,49,59,69]
Round 2
  [ 0,19,28,37,46,55,64]
  [ 1,10,29,38,47,56,65]
  [ 2,11,20,39,48,57,66]
  [ 3,12,21,30,49,58,67]
  [ 4,13,22,31,40,59,68]
  [ 5,14,23,32,41,50,69]
  [ 6,15,24,33,42,51,60]
  [ 7,16,25,34,43,52,61]
  [ 8,17,26,35,44,53,62]
  [ 9,18,27,36,45,54,63]
Round 3
  [ 0,14,29,35,43,51,68]
  [ 1,15,20,36,44,52,69]
  [ 2,16,21,37,45,53,60]
  [ 3,17,22,38,46,54,61]
  [ 4,18,23,39,47,55,62]
  [ 5,19,24,30,48,56,63]
  [ 6,10,25,31,49,57,64]
  [ 7,11,26,32,40,58,65]
  [ 8,12,27,33,41,59,66]
  [ 9,13,28,34,42,50,67]
Round 4
  [ 0,17,25,39,41,58,63]
  [ 1,18,26,30,42,59,64]
  [ 2,19,27,31,43,50,65]
  [ 3,10,28,32,44,51,66]
  [ 4,11,29,33,45,52,67]
  [ 5,12,20,34,46,53,68]
  [ 6,13,21,35,47,54,69]
  [ 7,14,22,36,48,55,60]
  [ 8,15,23,37,49,56,61]
  [ 9,16,24,38,40,57,62]
Round 5
  [ 0,16,23,31,48,54,67]
  [ 1,17,24,32,49,55,68]
  [ 2,18,25,33,40,56,69]
  [ 3,19,26,34,41,57,60]
  [ 4,10,27,35,42,58,61]
  [ 5,11,28,36,43,59,62]
  [ 6,12,29,37,44,50,63]
  [ 7,13,20,38,45,51,64]
  [ 8,14,21,39,46,52,65]
  [ 9,15,22,30,47,53,66]