从集合创建组而不重复过去的组

时间:2017-10-15 19:50:37

标签: python combinations

我试图创建一个程序,该程序从一个类生成学生组,但不创建之前创建的组。具体来说,我需要每周从同一组学生中创建2个新的学生实验室小组,并且我不会多次将同一个两个学生配对在一起。在前几周配对的学生将以某种方式作为输入。

过去的群体也需要排除他们的镜像,即如果[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

4 个答案:

答案 0 :(得分:1)

首先,我们需要将现有的组转换为元组。每个都需要另外排序,因为这是itertools.combinations生成它们的顺序。因此。

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_set的元素:

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)

答案 1 :(得分:1)

所以基本上你想要每次都覆盖所有项目,每个项目只选择一次,顺序并不重要。所以我从以前采取了一种全新的不同方法:

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,从列表中删除了排除(及其镜像)并对其进行了处理。现在唯一的技巧是确保我们不选择一对可以创建无解的方案 - 因为重叠对在覆盖所有项目时无法与其连接。因此,我使用递归和每一步我尝试使用该对获得解决方案,直到我们找到解决方案。

享受

答案 2 :(得分:0)

对于N选择2,我想我刚刚听到你的规范归结为:

itertools.combinations(students, r=2)

文档位于https://docs.python.org/3/library/itertools.html#itertools.permutations

在运行之前,随意随机排列整个列表。

只需维护一套描述之前实验室作业的集合,并测试该集合中的成员资格,以拒绝重复提案。

编辑:谢谢你,Antti Haapala,关于组合评论

  

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

我想我不太明白这个问题。假设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)

答案 3 :(得分:0)

如果您不需要随机返回该组,那么您可能只记得最后一组返回并保持"递增"下一组。下面的示例代码显示了如何为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