Algoritmo para criar um calendário escolar

95

Tenho me perguntado se existem soluções conhecidas para algoritmos de criação de um horário escolar. Basicamente, trata-se de otimizar a "dispersão de horas" (tanto no caso de professores como de classes) para determinadas associações classe-disciplina-professor. Podemos supor que temos conjuntos de classes, disciplinas de aula e professores associados entre si na entrada e que o horário deve caber entre 8h e 16h.

Acho que provavelmente não existe um algoritmo preciso para isso, mas talvez alguém conheça uma boa aproximação ou dicas para desenvolvê-lo.

cand
fonte
2
Obrigado por todas as respostas. Parece que o algoritmo requer mais investigação. Eu o consideraria um bom assunto para dissertação de mestrado ou pequena aplicação comercial. Se eu escrever um, informarei Você aqui;)
cand
10
Como Ian Ringrose, do StackOverflow, disse a outra pergunta, "ainda há muitos PHDs disponíveis no software de agendamento."
Reed Debaets

Respostas:

86

Este problema é NP-Completo !
Em suma, é preciso explorar todas as combinações possíveis para encontrar a lista de soluções aceitáveis. Por causa das variações nas circunstâncias em que o problema aparece em várias escolas (por exemplo: Existem restrições em relação às salas de aula ?, Algumas das classes são divididas em subgrupos algumas vezes ?, Este é um horário semanal? etc.) não existe uma classe de problema bem conhecida que corresponda a todos os problemas de escalonamento. Talvez o problema da mochila tenha muitos elementos de semelhança com esses problemas em geral.

Uma confirmação de que este é um problema difícil e para o qual as pessoas sempre buscam uma solução, é verificar esta lista (longa) de ferramentas de programação de software (principalmente comerciais)

Por causa do grande número de variáveis ​​envolvidas, a maior fonte das quais são, normalmente, os desejos do docente; -) ..., normalmente é impraticável considerar a enumeração de todas as combinações possíveis . Em vez disso, precisamos escolher uma abordagem que visite um subconjunto dos espaços de problema / solução.
- Algoritmos Genéticos , citados em outra resposta estão (ou, IMHO, parecem ) bem equipados para realizar este tipo de busca semi-guiada (O problema é encontrar uma boa função de avaliação para os candidatos a serem mantidos para a próxima geração)
- Gráfico Abordagens de reescrita também são úteis com este tipo de problemas de otimização combinatória.

Em vez de focar em implementações particulares de um programa gerador automático de agendamento, gostaria de sugerir algumas estratégias que podem ser aplicadas, no nível da definição do problema .
O raciocínio geral é que na maioria dos problemas de programação do mundo real, alguns compromissos serão necessários, nem todas as restrições, expressas e implícitas: serão satisfeitas totalmente. Portanto, ajudamos a nós mesmos:

  • Definir e classificar todas as restrições conhecidas
  • Reduzindo o espaço do problema, manualmente, fornecendo um conjunto de restrições adicionais .
    Isso pode parecer contra-intuitivo, mas, por exemplo, ao fornecer um cronograma inicial parcialmente preenchido (digamos, cerca de 30% dos intervalos de tempo), de uma forma que satisfaça totalmente todas as restrições e, ao considerar este cronograma parcial imutável, reduzimos significativamente o tempo / espaço necessário para produzir soluções candidatas.
    Outra forma de ajudar as restrições adicionais é, por exemplo, adicionar "artificialmente" uma restrição que impeça o ensino de algumas disciplinas em alguns dias da semana (se este for um horário semanal ...); esse tipo de restrição resulta na redução dos espaços de problema / solução, sem, normalmente, excluir um número significativo de bons candidatos.
  • Garantir que algumas das restrições do problema possam ser calculadas rapidamente. Isso geralmente está associado à escolha do modelo de dados usado para representar o problema; a ideia é poder optar (ou eliminar) rapidamente algumas das opções.
  • Redefinindo o problema e permitindo que algumas das restrições sejam quebradas, algumas vezes, (normalmente nos nós finais do gráfico). A ideia aqui é remover algumas das restrições para preencher os últimos espaços no cronograma, ou fazer com que o programa gerador automático de cronograma pare tímido de completar todo o cronograma, em vez de nos fornecer uma lista de uma dúzia ou mais plausível candidatos. Um humano geralmente está em uma posição melhor para completar o quebra-cabeça, conforme indicado, possivelmente quebrando algumas das restrições, usando informações que não são normalmente compartilhadas com a lógica automatizada (por exemplo, a regra "Sem matemática à tarde" pode ser quebrada ocasionalmente para a aula de "matemática e física avançada"; ou "É melhor quebrar um dos requisitos do Sr. Jones do que um da Sra. Smith ... ;-))

Ao revisar esta resposta, percebo que é muito tímido em fornecer uma resposta definitiva, mas ainda assim cheia de sugestões práticas. Espero que ajude, com o que é, afinal, um "problema difícil".

mjv
fonte
1
Resposta ótima, precisa e elaborada, obrigado pelas dicas e menção sobre NP-Completude (foi o meu palpite também).
cand
3
Você tem alguma citação que explique a NP-completude deste problema?
Don
49

É uma bagunça. uma bagunça real. Para complementar as respostas, já bastante completas, quero destacar minha experiência familiar. Minha mãe era professora e costumava participar do processo.

Acontece que ter um computador para fazer isso não é apenas difícil de codificar por si só, mas também porque existem condições que são difíceis de especificar para um programa de computador pré-preparado. Exemplos:

  • um professor ensina na sua escola e em outro instituto. É claro que se ele termina a aula lá às 10h30, ele não pode começar nas suas instalações às 10h30, porque precisa de algum tempo para se deslocar entre os institutos.
  • dois professores são casados. Em geral, é considerado uma boa prática não ter dois professores casados ​​na mesma classe. Esses dois professores devem, portanto, ter duas classes diferentes
  • dois professores são casados ​​e o filho frequenta a mesma escola. Novamente, você tem que evitar que os dois professores ensinem na classe específica onde seu filho está.
  • a escola tem instalações separadas, como um dia a aula é em um instituto e outro dia a aula é em outro.
  • a escola tem laboratórios compartilhados, mas esses laboratórios estão disponíveis apenas em alguns dias da semana (por razões de segurança, por exemplo, quando é necessário pessoal adicional).
  • alguns professores têm preferência pelo dia livre: alguns preferem na segunda-feira, alguns na sexta-feira, alguns na quarta-feira. Alguns preferem vir de manhã cedo, outros preferem vir mais tarde.
  • você não deve ter situações em que tenha uma aula de, digamos, história na primeira hora, depois três horas de matemática, depois outra hora de história. Não faz sentido para os alunos, nem para o professor.
  • você deve espalhar os argumentos uniformemente. Não faz sentido ter nos primeiros dias da semana apenas matemática e, no resto da semana, apenas literatura.
  • você deve dar a alguns professores duas horas consecutivas para fazer testes de avaliação.

Como você pode ver, o problema não é NP-completo, é NP-insano.

Então, o que eles fazem é ter uma grande mesa com pequenas inserções de plástico e mover as inserções até que um resultado satisfatório seja obtido. Eles nunca começam do zero: normalmente partem do cronograma do ano anterior e fazem ajustes.

Stefano Borini
fonte
12
"NP-insano" - ótimo nome;) Concordo que é um problema realmente complexo, obrigado pelos comentários sobre o tratamento "no mundo real" desse problema. Meu pai e minha namorada também são professores e sei que a maioria das escolas tem mesas com inserções de plástico - isso me levou a pensar em um algoritmo possível para esse problema - porque, se um homem conseguir resolver, talvez seja possível escrever como um algoritmo.
cand
10
o que você quer escrever é um sistema especialista: um sistema feito de um monte de regras heurísticas. Deixando os sistemas especialistas de lado, este é um campo em que os algoritmos genéticos estão entre as melhores apostas. A dificuldade não está em resolver o problema, não só. A dificuldade também está em expor o problema e suas limitações.
Stefano Borini
1
Você está certo, o problema requer o fornecimento de um conjunto complexo de condições e restrições a serem atendidas, provavelmente com classificação de solução "aceitável". Eu concordo com os algoritmos genéticos, eles devem se encaixar bem neste problema, também acho que será melhor começar a desenvolver com um conjunto simples de condições e aprimorá-lo conforme a resposta correta para eles for obtida.
cand
1
Você também pode traduzir diretamente essas restrições e objetivos no MAXSAT. Os algoritmos MAXSAT são geralmente mais confiáveis ​​do que os algoritmos genéticos, mas você pode ter uma explosão de cláusulas devido a objetivos como as aulas de matemática devem ser distribuídas ao longo da semana.
Jules
26

A Competição Internacional de Horários 2007 teve uma trilha de programação de aulas e uma trilha de programação de exames. Muitos pesquisadores participaram dessa competição. Muitas heurísticas e metaheurísticas foram tentadas, mas no final as metaheurísticas de busca local (como Tabu Search e Simulated Annealing) claramente venceram outros algoritmos (como algoritmos genéticos).

Dê uma olhada nas duas estruturas de código aberto usadas por alguns dos finalistas:

Geoffrey De Smet
fonte
17

Uma das minhas atribuições de meio período era uma geração de tabelas escolares de algoritmo genético.

A mesa inteira é um "organismo". Houve algumas mudanças e advertências na abordagem dos algoritmos genéticos genéricos:

  • Regras foram feitas para "tabelas ilegais": duas turmas na mesma sala de aula, um professor ensinando duas turmas ao mesmo tempo etc. Essas mutações foram consideradas letais imediatamente e um novo "organismo" brotou no lugar do "falecido" imediatamente. O inicial foi gerado por uma série de tentativas aleatórias para obter uma tentativa legal (embora sem sentido). A mutação letal não foi contabilizada na contagem de mutações na iteração.

  • Mutações "Exchange" eram muito mais comuns do que mutações "Modify". As mudanças ocorreram apenas entre partes do gene que faziam sentido - sem substituir um professor por uma sala de aula.

  • Pequenos bônus foram atribuídos para agrupar certas 2 horas juntas, para atribuir a mesma sala de aula genérica em sequência para o mesmo grupo, para manter as horas de trabalho do professor e carga de aula contínua. Bônus moderados foram atribuídos para dar salas de aula corretas para determinado assunto, mantendo as horas de aula dentro de obrigações (manhã ou tarde), e assim por diante. Os grandes bônus eram para atribuir o número correto de determinada matéria, dada a carga de trabalho de um professor, etc.

  • Os professores poderiam criar seus cronogramas de carga de trabalho de "quero trabalhar então", "tudo bem para trabalhar então", "não gosta de trabalhar então", "não posso trabalhar então", com os pesos apropriados atribuídos. 24 horas inteiras eram horas de trabalho legais, exceto que a noite era muito indesejável.

  • A função de peso ... oh sim. A função de peso era um produto enorme e monstruoso (como na multiplicação) de pesos atribuídos a características e propriedades selecionadas. Era extremamente íngreme, uma propriedade facilmente capaz de mudá-la em uma ordem de magnitude para cima ou para baixo - e havia centenas ou milhares de propriedades em um organismo. Isso resultou em números absolutamente ENORMES como os pesos e, como resultado direto, é necessário usar uma biblioteca bignum (gmp) para realizar os cálculos. Para um pequeno teste de cerca de 10 grupos, 10 professores e 10 salas de aula, o conjunto inicial começou com uma nota de 10 ^ -200 algo e terminou com 10 ^ + 300 algo. Era totalmente ineficiente quando era mais plano. Além disso, os valores se distanciaram muito mais com "escolas" maiores.

  • Em termos de tempo de computação, havia pouca diferença entre uma população pequena (100) em um longo tempo e uma população grande (10k +) em menos gerações. O cálculo ao longo do mesmo tempo produziu aproximadamente a mesma qualidade.

  • O cálculo (em algumas CPUs de 1 GHz) levaria cerca de 1h para se estabilizar perto de 10 ^ + 300, gerando cronogramas que pareciam muito bons, para o referido caso de teste 10x10x10.

  • O problema é facilmente paralelizável ao fornecer facilidade de rede que trocaria os melhores espécimes entre computadores que executam a computação.

O programa resultante nunca viu a luz do dia lá fora me dando uma boa nota para o semestre. Ele se mostrou promissor, mas nunca tive motivação suficiente para adicionar qualquer GUI e torná-la utilizável para o público em geral.

SF.
fonte
5
Então abra e anuncie e tente trazer alguns acadêmicos para isso? Reutilizá-lo para outros projetos? Tecnicamente, um programa como esse, apenas para 300 funcionários, valeria dinheiro para as escolas produzirem horários ideais, e eles não se importariam de gastar alguns dias para calcular geneticamente os horários ideais. Pense no processamento em lote. Olá contratos de hardware e software;)
jcolebrand
1
Parece bom! Gostaria de saber se a função de peso poderia ser feita com flutuadores no intervalo 0..1.
Craig McQueen
1
@Craig: Algo tão plano geraria uma população que se estagnou ou mesmo degenerou em qualidade com o tempo, já que mutações aleatórias contribuíram com mudanças mais negativas do que a reprodução / seleção poderia compensar. Somente funções de qualidade extremamente íngremes proporcionariam um crescimento estável Claro que a função poderia ser normalizada, mas ainda assim, um gene "notch melhor" precisava ter uma chance maior de sobreviver.
SF.
9

Esse problema é mais difícil do que parece.

Como outros já mencionaram, este é um problema NP-completo, mas vamos analisar o que isso significa.

Basicamente, significa que você deve examinar todas as combinações possíveis.

Mas "olhar para" não diz muito o que você precisa fazer.

É fácil gerar todas as combinações possíveis. Pode produzir uma grande quantidade de dados, mas você não deve ter muitos problemas para entender os conceitos dessa parte do problema.

O segundo problema é julgar se uma dada combinação possível é boa, má ou melhor do que a solução "boa" anterior.

Para isso, você precisa mais do que apenas "é uma solução possível".

Por exemplo, o mesmo professor trabalha 5 dias por semana durante X semanas seguidas? Mesmo que seja uma solução viável, pode não ser melhor do que alternar entre duas pessoas para que cada professor faça uma semana cada. Oh, você não pensou sobre isso? Lembre-se de que você está lidando com pessoas, não apenas com um problema de alocação de recursos.

Mesmo se um professor pudesse trabalhar em tempo integral por 16 semanas consecutivas, isso poderia ser uma solução abaixo do ideal em comparação com uma solução em que você tenta alternar entre os professores, e esse tipo de equilíbrio é muito difícil de incluir no software.

Para resumir, produzir uma boa solução para esse problema valerá muito, para muitas pessoas. Portanto, não é um problema fácil de analisar e resolver. Esteja preparado para definir alguns objetivos que não sejam 100% e chamá-los de "bons o suficiente".

Lasse V. Karlsen
fonte
1
Bem, eu diria que é bastante difícil saber todas as restrições no início, é preciso experiência e investigação do assunto. Prefiro dividir o problema em duas partes distintas e desenvolvê-las simultaneamente. Primeiro será a estrutura geral do algoritmo - dizendo como deve preencher a "próxima geração de cronograma", em vez de esboço do mecanismo, sem muita "lógica do assunto" por trás (provavelmente algoritmo genético). O segundo será um provedor de regras com um conjunto de restrições que verificam a "exatidão" do horário - pode ser simples no início e aprimorado posteriormente.
cand
8

Meu algoritmo de cronograma, implementado em FET (Free Timetabling Software, http://lalescu.ro/liviu/fet/ , um aplicativo de sucesso):

O algoritmo é heurístico. Eu o chamei de "troca recursiva".

Entrada: um conjunto de atividades A_1 ... A_n e as restrições.

Saída: um conjunto de tempos TA_1 ... TA_n (o intervalo de tempo de cada atividade. Os quartos são excluídos aqui, para simplificar). O algoritmo deve colocar cada atividade em um intervalo de tempo, respeitando as restrições. Cada TA_i está entre 0 (T_1) e max_time_slots-1 (T_m).

Restrições:

C1) Básico: lista de pares de atividades que não podem ser simultâneas (por exemplo, A_1 e A_2, porque têm o mesmo professor ou os mesmos alunos);

C2) Muitas outras restrições (excluídas aqui, para simplificar).

O algoritmo de tabela de horários (que denominei "troca recursiva"):

  1. Classifique as atividades, as mais difíceis primeiro. Não é uma etapa crítica, mas acelera o algoritmo talvez 10 vezes ou mais.
  2. Tente colocar cada atividade (A_i) em um intervalo de tempo permitido, seguindo a ordem acima, uma de cada vez. Procure um slot disponível (T_j) para A_i, no qual esta atividade pode ser colocada respeitando as restrições. Se houver mais slots disponíveis, escolha um aleatório. Se nenhum estiver disponível, faça a troca recursiva:

    a . Para cada intervalo de tempo T_j, considere o que acontece se você colocar A_i em T_j. Haverá uma lista de outras atividades que não concordam com esta mudança (por exemplo, a atividade A_k está no mesmo slot T_j e tem o mesmo professor ou os mesmos alunos que A_i). Mantenha uma lista de atividades conflitantes para cada intervalo de tempo T_j.

    b . Escolha um slot (T_j) com o menor número de atividades conflitantes. Digamos que a lista de atividades neste slot contenha 3 atividades: A_p, A_q, A_r.

    c . Coloque A_i em T_j e torne A_p, A_q, A_r não alocado.

    d . Tente recursivamente colocar A_p, A_q, A_r (se o nível de recursão não for muito grande, digamos 14, e se o número total de chamadas recursivas contadas desde a etapa 2) em A_i começou não é muito grande, digamos 2 * n), como na etapa 2).

    e . Se colocado A_p, A_q, A_r com sucesso, retorna com sucesso, caso contrário, tente outros intervalos de tempo (vá para a etapa 2 b) e escolha o próximo melhor intervalo de tempo).

    f . Se todos (ou um número razoável de) slots de tempo foram tentados sem sucesso, retorne sem sucesso.

    g . Se estivermos no nível 0 e não tivermos sucesso em colocar A_i, coloque-o como nos passos 2 b) e 2 c), mas sem recursão. Temos agora 3 - 1 = 2 mais atividades para realizar. Vá para a etapa 2) (alguns métodos para evitar ciclos são usados ​​aqui).

Liviu Lalescu
fonte
1
O FET foi muito útil para mim. Obrigado @Liviu Lalescu!
Noel Llevares
6

ATUALIZAÇÃO: dos comentários ... deve ter heurísticas também!

Eu escolheria Prolog ... então usaria Ruby ou Perl ou algo assim para limpar sua solução para uma forma mais bonita.

teaches(Jill,math).
teaches(Joe,history).

involves(MA101,math).
involves(SS104,history).

myHeuristic(D,A,B) :- [test_case]->D='<';D='>'.
createSchedule :- findall(Class,involves(Class,Subject),Classes),
                  predsort(myHeuristic,Classes,ClassesNew),
                  createSchedule(ClassesNew,[]).
createSchedule(Classes,Scheduled) :- [the actual recursive algorithm].

Estou (ainda) no processo de fazer algo semelhante a este problema, mas usando o mesmo caminho que acabei de mencionar. Prolog (como uma linguagem funcional) realmente torna mais fácil resolver problemas NP-Hard.

Reed Debaets
fonte
1
Prolog é certamente uma ótima linguagem para expressar os problemas necessários, no entanto, como você apontou: o problema É NP-completo, se não NP-Difícil. Isso significa que o Prolog pode não ser rápido o suficiente para uma implementação prática.
Poindexter
3
se tiver alguma coisa a ver com NP e não ficarmos satisfeitos por aproximação, qualquer algoritmo determinístico será exponencialmente
ruim
1
O objetivo, então, é implementar heurísticas eficazes ... por exemplo, um algoritmo simples de agendamento de 9 tarefas leva 3,078s para ser concluído, mas com uma heurística menor do WindowsFirst implementada, o mesmo problema leva apenas: 0,123s
Reed Debaets
2
HAHA, prólogo (sozinho) NUNCA, NUNCA RESOLVERÁ ISSO. Desculpe pelas letras maiúsculas, mas eu tive a mesma ideia 10 ou 15 anos atrás e falhei totalmente. Não que fosse lento, não. Simplesmente não conseguia resolver nenhum caso do mundo real;)!
Karussell
3

Trabalho em um mecanismo de agendamento amplamente usado que faz exatamente isso. Sim, é NP-completo; as melhores abordagens procuram aproximar uma solução ótima. E, claro, existem muitas maneiras diferentes de dizer qual é a "melhor" solução - é mais importante que seus professores estejam satisfeitos com seus horários, ou que os alunos participem de todas as aulas, por exemplo?

A questão absolutamente mais importante que você precisa resolver no início é o que torna uma maneira de programar este sistema melhor do que outra ? Isto é, se eu tenho um horário com a Sra. Jones ensinando Matemática aos 8 e o Sr. Smith ensinando Matemática aos 9, isso é melhor ou pior do que um com os dois ensinando Matemática aos 10? É melhor ou pior do que um com a Sra. Jones ensinando aos 8 e o Sr. Jones ensinando aos 2? Por quê?

O principal conselho que eu daria aqui é dividir o problema tanto quanto possível - talvez curso por curso, talvez professor por professor, talvez sala por sala - e trabalhar primeiro para resolver o subproblema. Lá você deve acabar com várias soluções para escolher e precisa escolher uma como a mais provável ideal. Em seguida, trabalhe para fazer com que os subproblemas "anteriores" levem em consideração as necessidades dos subproblemas posteriores ao pontuar suas soluções potenciais. Em seguida, talvez trabalhe em como se livrar de situações imprecisas (supondo que você não possa antecipar essas situações em subproblemas anteriores) quando chegar a um estado de "soluções sem validade".

Um passo de otimização de busca local é freqüentemente usado para "polir" a resposta final e obter melhores resultados.

Observe que normalmente estamos lidando com sistemas altamente restritos de recursos na programação escolar. As escolas não passam o ano com muitas salas vazias ou professores sentados na sala 75% do dia. As abordagens que funcionam melhor em ambientes ricos em soluções não são necessariamente aplicáveis ​​na programação escolar.

Tom Dibble
fonte
2

Eu projetei algoritmos comerciais para os horários das aulas e dos exames. Para o primeiro, usei programação inteira; para o segundo, uma heurística baseada na maximização de uma função objetivo escolhendo trocas de slots, muito semelhante ao processo manual original que havia sido desenvolvido. As principais coisas para que essas soluções sejam aceitas são a capacidade de representar todas as restrições do mundo real; e para que os cronômetros humanos não consigam ver maneiras de melhorar a solução. No final, a parte algorítmica foi bastante direta e fácil de implementar em comparação com a preparação dos bancos de dados, a interface do usuário, a capacidade de relatar estatísticas como utilização da sala, educação do usuário e assim por diante.

Permaquid
fonte
1

Geralmente, a programação de restrição é uma boa abordagem para esse tipo de problema de agendamento. Uma pesquisa sobre "programação de restrição" e agendamento ou "agendamento baseado em restrição" tanto no estouro de pilha quanto no Google irá gerar algumas boas referências. Não é impossível - é um pouco difícil pensar sobre o uso de métodos de otimização tradicionais, como otimização linear ou inteira. Uma saída seria - existe uma programação que satisfaça todos os requisitos? Isso, por si só, é obviamente útil.

Boa sorte !

Grembo
fonte
1

Você pode lidar com isso com algoritmos genéticos, sim. Mas você não deve :). Pode ser muito lento e o ajuste dos parâmetros pode ser muito demorado, etc.

Existem outras abordagens bem-sucedidas. Tudo implementado em projetos de código aberto:

  • Abordagem baseada em restrições
    • Implementado em UniTime (não realmente para escolas)
    • Você também pode ir mais longe e usar a programação inteira. Feito com sucesso na Universidade Udine e também na Universidade Bayreuth (estive envolvido lá) usando o software comercial (ILOG CPLEX)
    • Abordagem baseada em regras com heuristisc - Consulte planejador Drools
  • Heurísticas diferentes - FET e minha própria

Veja aqui uma lista de software de tabela de horários

Karussell
fonte
0

Acho que você deve usar algoritmo genético porque:

  • É mais adequado para grandes instâncias de problemas.
  • Isso resulta em complexidade de tempo reduzida no preço de uma resposta imprecisa (não é a melhor opção)
  • Você pode especificar restrições e preferências facilmente ajustando punições de aptidão para aqueles que não foram cumpridos.
  • Você pode especificar o limite de tempo para a execução do programa.
  • A qualidade da solução depende de quanto tempo você pretende gastar resolvendo o programa.

    Definição de Algoritmos Genéticos

    Tutorial de Algoritmos Genéticos

    Projeto de agendamento de aulas com GA

Também dê uma olhada em: uma pergunta semelhante e outra

Betamoo
fonte
0

Não sei se alguém vai concordar com este código, mas desenvolvi este código com a ajuda do meu próprio algoritmo e está trabalhando para mim em ruby. Espero que ajude os que estão procurando por ele no código a seguir the periodflag, dayflag subjectflag e teacherflag são o hash com o id correspondente e o valor do flag, que é booleano. Qualquer problema entre em contato comigo ....... (-_-)

periodflag.each do | k2, v2 |

            if(TimetableDefinition.find(k2).period.to_i != 0)
                subjectflag.each do |k3,v3|
                    if (v3 == 0)
                        if(getflag_period(periodflag,k2))
                            @teachers=EmployeesSubject.where(subject_name: @subjects.find(k3).name, division_id: division.id).pluck(:employee_id)
                            @teacherlists=Employee.find(@teachers)
                            teacherflag=Hash[teacher_flag(@teacherlists,teacherflag,flag).to_a.shuffle] 
                            teacherflag.each do |k4,v4|
                                if(v4 == 0)
                                    if(getflag_subject(subjectflag,k3))
                                        subjectperiod=TimetableAssign.where("timetable_definition_id = ? AND subject_id = ?",k2,k3)
                                        if subjectperiod.blank?
                                            issubjectpresent=TimetableAssign.where("section_id = ? AND subject_id = ?",section.id,k3)
                                            if issubjectpresent.blank?
                                                isteacherpresent=TimetableAssign.where("section_id = ? AND employee_id = ?",section.id,k4)
                                                if isteacherpresent.blank?
                                                    @finaltt=TimetableAssign.new
                                                    @finaltt.timetable_struct_id=@timetable_struct.id
                                                    @finaltt.employee_id=k4
                                                    @finaltt.section_id=section.id
                                                    @finaltt.standard_id=standard.id
                                                    @finaltt.division_id=division.id
                                                    @finaltt.subject_id=k3
                                                    @finaltt.timetable_definition_id=k2
                                                    @finaltt.timetable_day_id=k1
                                                    set_school_id(@finaltt,current_user)
                                                    if(@finaltt.save)

                                                        setflag_sub(subjectflag,k3,1)
                                                        setflag_period(periodflag,k2,1)
                                                        setflag_teacher(teacherflag,k4,1)
                                                    end
                                                end
                                            else
                                                @subjectdetail=TimetableAssign.find_by_section_id_and_subject_id(@section.id,k3)
                                                @finaltt=TimetableAssign.new
                                                @[email protected]_struct_id
                                                @[email protected]_id
                                                @finaltt.section_id=section.id
                                                @finaltt.standard_id=standard.id
                                                @finaltt.division_id=division.id
                                                @[email protected]_id
                                                @finaltt.timetable_definition_id=k2
                                                @finaltt.timetable_day_id=k1
                                                set_school_id(@finaltt,current_user)
                                                if(@finaltt.save)

                                                    setflag_sub(subjectflag,k3,1)
                                                    setflag_period(periodflag,k2,1)
                                                    setflag_teacher(teacherflag,k4,1)
                                                end
                                            end
                                        end
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end

fonte