-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
290 lines (212 loc) · 9.08 KB
/
main.py
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
from fastapi import FastAPI
from pydantic import BaseModel
import time
from typing import List
import random
import matplotlib.pyplot as plt
def converter_minutos(hora):
t = time.strptime(hora, "%H:%M")
minutos = t[3] * 60 + t[4]
return minutos
def calcular_disciplinas_dia(horario, segmentacao_horario):
inicio_aula, fim_aula = horario.horario_inicio, horario.horario_fim
inicio_almoco, fim_almoco = horario.almoco.inicio, horario.almoco.fim
duracao_dia = converter_minutos(fim_aula) - converter_minutos(inicio_aula)
intervalo_almoco = converter_minutos(fim_almoco) - converter_minutos(inicio_almoco)
return ((duracao_dia - intervalo_almoco) // segmentacao_horario) + 1
def calcular_horas_vagas(aulas):
disciplinas = set(aulas)
aula_vaga = 0
for disciplina in disciplinas:
if disciplina == 0: # Ignora aulas vagas
continue
aula_atual = proxima_aula = 0
while (
aulas[proxima_aula:].count(disciplina) > 1
): # Calcula horas vagas de uma disciplina
aula_atual = aulas.index(disciplina, proxima_aula)
proxima_aula = aulas.index(disciplina, aula_atual + 1)
aula_vaga += proxima_aula - (aula_atual + 1)
return aula_vaga
def calcular_restricao_3(aulas, gene_almoco):
if aulas[gene_almoco - 1] == aulas[gene_almoco]:
return 1
return 0
class Almoco(BaseModel):
inicio: str
fim: str
class Horario(BaseModel):
horario_inicio: str
horario_fim: str
duracao_hora_aula: int
intervalo_minutos_entre_aulas: int
almoco: Almoco
dias_semana: List[str]
class Disciplina(BaseModel):
nome: str
restrição_horario: str
sigla: str
class Curso(BaseModel):
quantidade_turmas: int
maximo_aulas_dia: int
disciplinas: List[Disciplina]
class Data(BaseModel):
curso: Curso
horario: Horario
# comando para rodar : uvicorn main:app --reload
app = FastAPI()
@app.post("/gerador_qts/")
async def gera_qts(data: Data):
# name_days_week = ['Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado', 'Domingo']
dias_semana = data.horario.dias_semana
disciplinas = {0: ("NA", "Horário Vago")}
for index, disciplina in enumerate(data.curso.disciplinas):
disciplinas[index + 1] = (disciplina.sigla, disciplina.nome)
hora_aula = data.horario.duracao_hora_aula * 60
intervalo_aula = data.horario.intervalo_minutos_entre_aulas
segmentacao_horario = (
hora_aula + intervalo_aula
) # Divisão do horário no QTS (minutos)
espacos_totais = calcular_disciplinas_dia(
data.horario, segmentacao_horario
) # Retorna quantidade de sessões destinadas às disciplinas pela manhã
inicio_aula = converter_minutos(data.horario.horario_inicio)
inicio_almoco = converter_minutos(data.horario.almoco.inicio)
aulas_manha = (
((inicio_almoco - inicio_aula) // segmentacao_horario) + 1
) // data.curso.maximo_aulas_dia
gene_almoco = aulas_manha # Gene inicial é o zero!
disciplinas_dia = espacos_totais // data.curso.maximo_aulas_dia
dominio = [(0, len(disciplinas) - 1)] * (
data.curso.quantidade_turmas * len(dias_semana) * disciplinas_dia
)
def gerar_cromossomo(
turmas=data.curso.quantidade_turmas,
qtd_dias_semana=len(dias_semana),
disciplinas_dia=disciplinas_dia,
dominio=dominio[0],
):
tamanho_cromossomo = turmas * qtd_dias_semana * disciplinas_dia
cromossomo = []
for i in range(tamanho_cromossomo):
cromossomo += [random.randint(dominio[0], dominio[1])]
return cromossomo
constante_objetivo = 10
def avaliar_objetivos(aulas_dia):
desvio = 0
desvio += calcular_horas_vagas(aulas_dia) # Objetivo 1
return constante_objetivo * desvio
def aplicar_penalidades(aulas_dia):
desvio, constante = 0, 100
desvio += calcular_restricao_3(aulas_dia, gene_almoco) # Restrição 3
return constante * desvio
def funcao_avaliacao(calendario):
pontuacao = 0
for i in range(0, len(calendario), disciplinas_dia):
aulas_dia = calendario[i : i + disciplinas_dia]
pontuacao += avaliar_objetivos(
aulas_dia
) # Pontuar pelo nível de cumprimento dos objetivos
pontuacao += aplicar_penalidades(
aulas_dia
) # Penalizar descumprimento das restrições
return pontuacao
def imprimir_qts(individuo):
dia_id = 0
espera_total = 0
qtd_dias_semana = len(dias_semana)
dados_qts = {}
for i in range(data.curso.quantidade_turmas):
inicio_turma = i * (disciplinas_dia * qtd_dias_semana)
dados_dia = {}
for j in range(qtd_dias_semana):
dia = dias_semana[j]
comeco_semana = inicio_turma + j * disciplinas_dia
disciplinas_semana = individuo[
comeco_semana : comeco_semana + disciplinas_dia
] # coleta as disciplinas da semana
expandir_grupos = [
info
for aula in disciplinas_semana
for info in [aula] * data.curso.maximo_aulas_dia
] # expõem a quantidade real de aulas
decodificar_disciplinas = [
disciplinas[disciplina][0] for disciplina in expandir_grupos
] # traduz números em disciplinas
decodificar_disciplinas.insert(
gene_almoco * data.curso.maximo_aulas_dia, "Almoço"
) # inserção do horário de almoço
dados_dia[dia] = {", ".join(decodificar_disciplinas)}
dia_id += 1 # próximo dia da semana
espera_total += (
avaliar_objetivos(disciplinas_semana) // constante_objetivo
) * data.curso.maximo_aulas_dia
dados_qts[i + 1] = dados_dia
dados_qts["espera_total"] = espera_total
return dados_qts
individuo = gerar_cromossomo()
def mutacao(passo, calendario, probabilidade, dominio=dominio):
gene = random.randint(
0, len(dominio) - 1
) # Escolha do gene que sofrerá a mutação
mutante = calendario
# Joga um número aleatório para verificar se haverá mutação.
if random.random() < probabilidade:
if calendario[gene] != dominio[gene][0]: # Verifica borda inferior
mutante = (
calendario[0:gene]
+ [calendario[gene] - passo]
+ calendario[gene + 1 :]
)
elif calendario[gene] != dominio[gene][1]: # Verifica borda superior
mutante = (
calendario[0:gene]
+ [calendario[gene] + passo]
+ calendario[gene + 1 :]
)
return mutante
def crossover(individuo1, individuo2, dominio=dominio):
gene = random.randint(1, len(dominio) - 2)
return individuo1[0:gene] + individuo2[gene:]
def algoritmo_genetico(
tamanho_populacao=10,
passo=1,
elitismo=0.2,
numero_geracoes=100,
probabilidade_mutacao=0.05,
dominio=dominio,
):
populacao = []
for i in range(tamanho_populacao):
# Cria aleatoriamente os indivíduos
individuo = gerar_cromossomo()
populacao.append(individuo)
numero_elitismo = int(elitismo * tamanho_populacao)
melhor_fit = [] # Guardar o melhor de cada interação
for i in range(numero_geracoes):
custos = [
(funcao_avaliacao(individuo), individuo) for individuo in populacao
]
custos.sort()
individuos_ordenados = [individuo for (custo, individuo) in custos]
populacao = individuos_ordenados[
0:numero_elitismo
] # só pega o percentual dos elementos baseados no elisitmo
while len(populacao) < tamanho_populacao:
i1 = random.randint(0, numero_elitismo)
i2 = random.randint(0, numero_elitismo)
novo_individuo = crossover(
individuos_ordenados[i1], individuos_ordenados[i2]
)
mutacao_novo_individuo = mutacao(
passo, novo_individuo, probabilidade_mutacao
)
populacao.append(mutacao_novo_individuo)
melhor_fit.append(custos[0][0])
# Plotar o gráfico
plt.plot(melhor_fit)
return custos[0][1]
solucao = algoritmo_genetico(tamanho_populacao=10, numero_geracoes=400)
qts = imprimir_qts(solucao)
qts["avaliacao"] = funcao_avaliacao(solucao)
return qts