Python 从集合创建组而不重复以前的组

Python 从集合创建组而不重复以前的组,python,combinations,Python,Combinations,我试图创建一个程序,从一个类中生成学生组,但不创建以前创建过的组。具体来说,我需要每周从同一组学生中创建两个新的学生实验组,我尽量不让同两个学生多次配对。前几周配对的学生将以某种方式作为输入 过去的组也需要排除镜像,即如果[1,2]是过去的组,[2,1]也是过去的组 我的程序如下。它解决了问题,但我想它效率很低。如果是更好的解决方案,我会接受完全不同的代码 import numpy,random from itertools import combinations class_list="""a

我试图创建一个程序,从一个类中生成学生组,但不创建以前创建过的组。具体来说,我需要每周从同一组学生中创建两个新的学生实验组,我尽量不让同两个学生多次配对。前几周配对的学生将以某种方式作为输入

过去的组也需要排除镜像,即如果[1,2]是过去的组,[2,1]也是过去的组

我的程序如下。它解决了问题,但我想它效率很低。如果是更好的解决方案,我会接受完全不同的代码

import numpy,random
from itertools import combinations
class_list="""a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np
"""
students=class_list.splitlines()
#print len(students),students
combs=[map(int, comb) for comb in combinations(range(len(students)), 2)]
#print combs
done_list=[[0,4],[1,6],[2,13],[3,12],[8,10],[11,14],[15,9],
           [0,13],[1,4],[2,7],[3,12],[5,6],[8,10],[14,15],
           [0,1],[2,3],[4,5],[6,7],[8,9],[10,11],[12,15],[13,14],
           [0,2],[1,3],[4,6],[5,7],[8,14],[10,9],[12,11],[15,13]]
for i_done in done_list:
    if i_done in combs:
        combs.remove(i_done)
f_done=False
while(1):
    if f_done:
        break
    final_list=[]
    final_list_used_students=[]
    for _i in range(len(students)/2):
        rand_i=random.randint(0,len(combs)-1)
        if combs[rand_i][0] not in final_list_used_students and combs[rand_i][1] not in final_list_used_students:
            final_list.append(combs[rand_i])
            final_list_used_students.append(combs[rand_i][0])
            final_list_used_students.append(combs[rand_i][1])
        if len(final_list_used_students)==len(students):
            f_done=True
            break
print final_list

对于N choose 2,我想我刚刚听说您的规格可以归结为:

itertools.combinations(students, r=2)
文档位于

在运行整个列表之前,可以随意排列整个列表

只需维护一组描述先前实验任务的集合,并测试组合在该集合中的成员资格,以拒绝重复的建议

编辑:谢谢你,安蒂·哈帕拉,谢谢你的评论

如何搜索此新集合(不包含折扣组)并创建集合

我想我不太明白这个问题。假设
history
是一个包含历史学生对的集合,其中一对总是按排序顺序出现。然后就是询问发电机和过滤的问题,是吗

shuffled_students = [students[i]
                     for i in numpy.random.permutation(len(students))]
for pair in itertools.combinations(shuffled_students, r=2):
    pair = sorted(pair)
    if pair in history:
        continue
    history.add(pair)
    schedule_this(pair)

首先,我们需要将已经存在的组转换为一组元组。每个都需要额外排序,因为这是
itertools.compositions
生成它们的顺序。因此

done_list=[[0,4],[1,6],[2,13],[3,12],[8,10],[11,14],[15,9], #first old set
           [0,13],[1,4],[2,7],[3,12],[5,6],[8,10],[14,15],#2nd old set
           [0,1],[2,3],[4,5],[6,7],[8,9],[10,11],[12,15],[13,14],#3rd old set
           [0,2],[1,3],[4,6],[5,7],[8,14],[10,9],[12,11],[15,13]]#4th old set

done_set = {tuple(sorted(i)) for i in done_list}
然后我们可以生成一个生成函数,该函数只生成非
done\u集成员的元素

from itertools import combinations

def unseen_combinations(items, n):
    for i in combinations(items, n):
        if i not in done_set:
            done_set.add(i)
            yield i


for combination in unseen_combinations(students, 2):
    print(combination)

因此,基本上您希望每次都覆盖所有项目,其中每个项目只选择一次,并且顺序并不重要。因此,我采取了一种与以前完全不同的新方法:

import itertools


def find_path(optional_pairs, num_pairs, result, used_population):
    if num_pairs == 0:
        return result

    while optional_pairs:
        _pair = optional_pairs.pop(0)
        if _pair[0] in used_population or _pair[1] in used_population:
            continue

        # Try omitting this _pair
        pairs = list(optional_pairs)
        result2 = find_path(pairs, num_pairs, list(result), list(used_population))
        if result2:
            return result2

        # Try adding pair to path
        used_pop = list(used_population)
        used_pop.append(_pair[0])
        used_pop.append(_pair[1])
        result2 = list(result)
        result2.append(_pair)

        pairs = list(optional_pairs)
        return find_path(pairs, num_pairs - 1, result2, used_pop)

    return []


def get_duos(population, excluded_duos):
    excluded_duos = excluded_duos + [(x[1], x[0]) for x in excluded_duos]
    all_combinations = itertools.permutations(population, 2)

    optional_pairs = set(all_combinations) - set(excluded_duos)

    return find_path(list(optional_pairs), len(population) / 2, [], [])


print get_duos(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], [('a', 'c'), ('b', 'g'), ('f', 'd'), ('e', 'h'), ('b', 'f'), ('g', 'c'), ('a', 'e'), ('h', 'd')])
我使用了另一个答案中提到的
itertools.permutations
,从列表中删除了被排除的(及其镜像),并进行了处理。现在唯一的诀窍是确保我们不会选择一对可以创建“无”解决方案的配对,因为还押配对在覆盖所有项目时无法与其连接。因此,我使用递归,每一步我都会尝试得到一个有对和无对的解,直到找到解为止

import numpy,random
from itertools import combinations
class_list="""a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np
"""
students=class_list.splitlines()
#print len(students),students
combs=[map(int, comb) for comb in combinations(range(len(students)), 2)]
#print combs
done_list=[[0,4],[1,6],[2,13],[3,12],[8,10],[11,14],[15,9],
           [0,13],[1,4],[2,7],[3,12],[5,6],[8,10],[14,15],
           [0,1],[2,3],[4,5],[6,7],[8,9],[10,11],[12,15],[13,14],
           [0,2],[1,3],[4,6],[5,7],[8,14],[10,9],[12,11],[15,13]]
for i_done in done_list:
    if i_done in combs:
        combs.remove(i_done)
f_done=False
while(1):
    if f_done:
        break
    final_list=[]
    final_list_used_students=[]
    for _i in range(len(students)/2):
        rand_i=random.randint(0,len(combs)-1)
        if combs[rand_i][0] not in final_list_used_students and combs[rand_i][1] not in final_list_used_students:
            final_list.append(combs[rand_i])
            final_list_used_students.append(combs[rand_i][0])
            final_list_used_students.append(combs[rand_i][1])
        if len(final_list_used_students)==len(students):
            f_done=True
            break
print final_list

享受

如果您不需要随机返回组,那么您可以只记住最后返回的组,并继续“递增”下一组。下面的示例代码显示了如何为50名学生做到这一点:

student_count = 50
students_nos = range(0, student_count)
current_group = (0, 1)
group_exhausted = False

def get_next_group():
    global current_group
    global group_exhausted
    if group_exhausted:
        return None
    ret = current_group
    if (current_group[0] == students_nos[student_count - 2]) and (current_group[1] == students_nos[student_count - 1]):
        group_exhausted = True
    if current_group[1] == students_nos[student_count - 1]:
        current_group = (current_group[0] + 1, current_group[0] + 2)
    else:
        current_group = (current_group[0], current_group[1] + 1)
    return ret

# Exmpale run.....
while True:
    cur = get_next_group()
    if cur is None:
        break
    print cur

什么团体?2-组合?为什么这是精心设计的代码,以及为什么您要使用Python 2-这有特殊要求吗?精心设计是因为这是我的要求,尝试给出我的示例,如果可以轻松修改以生成解决方案,那么我建议您立即切换-Python 3是新手的首选语言。无论如何,您真的需要随机-为什么不排序?可能相关:这会查找组,但不会对已使用的组进行折扣。@您可以创建一组已经看到的组,并丢弃那些已经是该组成员的组。是的,我可以使用set.remove(折扣组)进行此操作。但是我如何搜索这个新的集合(不包含折扣组)并创建一组[total students/2](此处为8)组学生。这就是我正在努力解决的问题。还有排列->组合这会产生尚未使用的组合。但是我如何搜索这个新的集合(不包含折扣组)并创建一组[total students/2](此处为8)组学生。这就是我正在努力解决的问题。我的解决方案只是循环所有可能的组合,并将它们随机添加到集合中,如果该组中的学生尚未添加到集合中。然后,当最终找到匹配项时,该选项退出;需要几秒钟的时间。这是一个很小的问题,它不会打折彼此为镜像的组,这会打折[1,2],但不会打折[2,1]。如果我尝试进行更改以添加所有镜像组,它会卡在while循环中。