problema de satisfação de restrição faltando uma restrição

13

Sou tutor de práticas de laboratório na universidade, com base nos comentários dos alunos do ano passado, queríamos que eu e meu chefe os abordássemos. Meu chefe optou por escrever um script C e eu escolhi python (python-constraint) para tentar resolver nosso problema.

Informações

  • Existem 6 sessões
  • Existem 4 papéis
  • Existem 6 práticas
  • Existem 32 alunos
  • Há 4 alunos por equipe

Problema:

Designe cada aluno para 4 funções, em 4 práticas em 4 sessões diferentes.

Restrições :

  1. Os alunos devem desempenhar um papel uma vez
  2. Os alunos devem praticar 4 práticas diferentes em 6
  3. Os alunos devem praticar apenas uma prática por sessão
  4. O aluno deve encontrar o mesmo companheiro apenas uma vez

Modelos :

Aqui está o modelo que eu sinto com os alunos, onde cada equipe é composta por 4 alunos, as posições [0, 1, 2 ou 3] são funções atribuídas a eles. Cada posição disponível está numerada de 1 a 128

[# Semester
   [ # Session
     [ # Practice/Team
1, 2, 3, 4],
  [5, 6, 7, 8],
  [9, 10, 11, 12],
  [13, 14, 15, 16],
  [17, 18, 19, 20],
  [21, 22, 23, 24]],
 [[25, 26, 27, 28],
  [29, 30, 31, 32],
  [33, 34, 35, 36],
  [37, 38, 39, 40],
  [41, 42, 43, 44],
  [45, 46, 47, 48]],
 [[49, 50, 51, 52],
  [53, 54, 55, 56],
  [57, 58, 59, 60],
  [61, 62, 63, 64],
  [65, 66, 67, 68],
  [69, 70, 71, 72]],
 [[73, 74, 75, 76],
  [77, 78, 79, 80],
  [81, 82, 83, 84],
  [85, 86, 87, 88],
  [89, 90, 91, 92],
  [93, 94, 95, 96]],
 [[97, 98, 99, 100],
  [101, 102, 103, 104],
  [105, 106, 107, 108],
  [109, 110, 111, 112]],
 [[113, 114, 115, 116],
  [117, 118, 119, 120],
  [121, 122, 123, 124],
  [125, 126, 127, 128]]]

Em outras palavras :

Esta é uma sessão:

 [[1, 2, 3, 4],
  [5, 6, 7, 8],
  [9, 10, 11, 12],
  [13, 14, 15, 16],
  [17, 18, 19, 20],
  [21, 22, 23, 24]],

Essas equipes fazem a mesma prática:

[
    [1, 2, 3, 4],
    [25, 26, 27, 28],
    [49, 50, 51, 52],
    [73, 74, 75, 76],
    [97, 98, 99, 100],
    [113, 114, 115, 116]
]

Essas posições desempenham o mesmo papel:

[
   1,
   5,
   9,
   13,
   17,
   21,
   25,
   ...
]

O que tenho até agora:

Usando python-constraint, pude validar as três primeiras restrições:

Valid solution : False
            - sessions  : [True, True, True, True, True, True]
            - practices : [True, True, True, True, True, True]
            - roles     : [True, True, True, True]
            - teams     : [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False]

Para aqueles que podem ser interessantes, eu simplesmente faço assim:

Para cada condição eu uso AllDifferentConstraint . Por exemplo, para uma sessão eu faço:

problem.addConstraint(AllDifferentConstraint(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24])

Não consigo encontrar uma maneira de restringir a equipe, minha última tentativa no geral semesterfoi a seguinte:

    def team_constraint(self, *semester):
        students = defaultdict(list)

        # get back each teams based on the format [# Semester [ #Session [# Practice/Team ... 
        teams = [list(semester[i:i+4]) for i in range(0, len(semester), 4)]

        # Update Students dict with all mate they work with
        for team in teams:
            for student in team:
                students[student] += [s for s in team if s != student]

        # Compute for each student if they meet someone more than once 
        dupli = []
        for student, mate in students.items():
            dupli.append(len(mate) - len(set(mate)))

        # Loosly constraint, if a student meet somone 0 or one time it's find
        if max(dupli) >= 2:
            print("Mate encounter more than one time", dupli, min(dupli) ,max(dupli))
            return False
        pprint(students)
        return True

Questões :

  1. É possível fazer o que eu quero para as condições da equipe? O que quero dizer é que não tenho idéia se é possível atribuir 12 companheiros a cada aluno e cada um deles encontrar o mesmo companheiro apenas uma vez.
  2. Para a restrição de equipe, perdi um algoritmo de melhor desempenho?
  3. Qualquer pist que eu possa seguir?
Florian Bernard
fonte
11
Por que os dois últimos conjuntos de sessões têm uma forma e (4, 4)não (4, 6)como os outros?
r.ook
É para combinar com o fato de que este curso é apenas um crédito e exige muito trabalho, então meu chefe deseja que os alunos façam apenas quatro práticas. Então, nós criamos isso, temos 32 alunos, que devem fazer 4 práticas (128 posições).
Florian Bernard
11
Eu tentaria uma abordagem aleatória e de força bruta. Faça como uma permutação em que você escolhe a Sessão 1: Função 1 Aluno 1 Prática 1 ... o mesmo com 2 a 4. Em seguida, aumente a cada 6 sessões, descarte os alunos já encontrados. O mesmo acontece com o aleatório. Por que 128 posições e não usar por número de alunos da Sessão 32 o máximo em diferentes permutações? Talvez em stackMath eles podem te dizer se isso é possível combinação / permutação
Cristo
Atualmente, o método de força bruta funciona, meu chefe voltou para mim com seu script e seu trabalho muito bem. Mas ainda quero usar python.
Florian Bernard

Respostas:

2

A pergunta principal seria respondida com algo como ...

   def person_works_with_different():
        # over all the sessions, each person works with each other person no more than once.
        # 'works with' means in 'same session team'
        for p in all_people:
            buddy_constraint = []
            for s in all_sessions:
                for g in all_teams:
                    p_list = [pv[k] for k in filter(lambda i: i[P] == p and i[S] == s and i[G] == g, pv)]
                    for o in all_people:
                        if o != p:  # other is not person
                            o_list = [self.pv[k] for k in filter(lambda i: i[self.P] == o and i[self.S] == s and i[self.G] == g, self.pv)]
                            tmp = model.NewBoolVar('')
                            buddy_constraint.append(tmp)
                            model.Add(sum(o_list) == sum(p_list)).OnlyEnforceIf(tmp)
                            # tmp is set only if o and p are in the same session/team
            # The number of times a student gets to take part is the number of roles.
            # The size of the group controlled by the number of roles
            model.Add(sum(buddy_constraint) = all_roles * (all_roles - 1)) 

Edição adicionada

Ontem, olhei para o seu problema - (reconhecidamente não muito tempo, pois tenho muito trabalho no momento) e ...

Antes de tudo, vejo que sua entidade de 'equipe' é praticamente o que chamei de entidade de 'ação' e, em retrospecto, acho que 'equipe' (ou 'grupo') era uma palavra melhor para isso.

Se você ainda estiver achando difícil as restrições, sugiro que você as rompa e trabalhe nelas individualmente - particularmente as restrições de equipe / pessoa / sessão, seguidas pelas restrições de função / tarefa.

/ Edição adicionada

team: a gathering of 4 persons during a session
person (32): a participant of a team
session (6): time: eg, 8am -10am
role (4): what responsibility a person has in an action
task (6): type of action

A person does:
 0..1 action per session-group
 1 role per action
 1 task per action
 0..1 of each task
 1 of each role in an action
 4 persons in an action

A person meets each other person 0..1 times
An action requires exactly 4 people

Eu tive um problema semelhante recentemente e, no final, virei para o OR-tools. https://developers.google.com/optimization/cp/cp_solver

Particularmente, dê uma olhada no problema de agendamento da enfermeira: https://developers.google.com/optimization/scheduling/employee_scheduling#nurse_scheduling

De qualquer forma, o problema não é muito complexo, então talvez usar um solucionador seja um exagero para você.

Da mesma forma, para esse tipo de problema, pode ser melhor usar um ditado com chave de tupla para armazenar suas variáveis, em vez de listas aninhadas:

{Equipe, Sessão, Pessoa: BoolVar}

O principal motivo é que você pode aplicar restrições por meio de filtros, o que é muito mais fácil do que ter que manipular listas aninhadas, por exemplo, para aplicar uma restrição entre pessoas / equipes, você pode fazer (onde person é o índice 2 e team é o índice 0):

for p in all_persons:
    for t in all_teams:
        stuff = [b_vars[k] for k in filter(lambda i: i[2] == p and i[0] == t, b_vars)]
        model.Add(sum(stuff) == 4)  # persons per team == 4
Konchog
fonte
11
Obrigado, pelo loop for, você quis dizer p for p in all_people?
Florian Bernard
11
Sim, desculpe-me! Eu 'traduzi' meus nomes para o seu modelo, mas estava no trabalho, então foi um pouco rápido.
21919 Konchog
11
Além disso, a lista de discussão é realmente de suporte nas ferramentas OR. Se você precisar de ajuda com a modelagem do seu problema, eles apontarão para um exemplo de código ou fornecerão uma ótima idéia sobre como definir restrições de grupo / dependência
Konchog
Sinto muito, mas sua solução para a cabeça é difícil de seguir, de onde vem o eu? E quais são as variáveis ​​P, S e G? O que é pv? Obrigado pela ajuda.
Florian Bernard
0

Apenas uma idéia do algoritmo de permutação, para cada iteração, poderia ser focada em um de cada aluno ou em uma de cada sessão:

Session 1:
Roles
1,2,3,4
Students
1,2,3,4

(Note is 1st permutation 1234)

Sess 2 for student 1
Roles 1234
Students 5,1,7,6

Aqui o aluno 2 substitui o aluno 1 na sessão 1 e continua assim

Roles 1234
St 2,5,6,7 

Continue com o aluno 1 S3 R 1234 St 10,9,1,8

S4
R 1234
St 11,12,13,1

No final, você remove as interações do aluno 1, como nas permutações da próxima iteração, e remove a corrente.

É como um cubo de rubiks.

Se você codificar isso ou souber algum código com esse algo, me avise.

Talvez com as permutações de itertools

Sessões sendo> do que práticas que acredito não são tão relevantes quanto seu número. Apenas um pouco de piscina para levar mais quando você acabar ou mais espaço para a rotação. Talvez pudesse simplificar o problema primeiro com o objetivo de 4 sessões = práticas?

Cristo
fonte