refactor: converte modelagem matemática de horas decimais para minutos inteiros

This commit is contained in:
2026-06-07 20:48:25 -03:00
parent da42ebdad1
commit c21cc4eece
2 changed files with 146 additions and 146 deletions

View File

@@ -311,15 +311,15 @@ for r in rotas_lista:
aeroportos_malha.add(d)
aeroportos_malha = list(aeroportos_malha)
# Matriz de tempo de voo entre todos os nós da malha (para reposicionamento)
tempos_voo = {}
# Matriz de tempo de voo entre todos os nós da malha em MINUTOS (inteiro)
tempos_voo_min = {}
for o in aeroportos_malha:
for d in aeroportos_malha:
if o == d:
tempos_voo[(o, d)] = 0
tempos_voo_min[(o, d)] = 0
else:
dist = calc_distancia(o, d)
tempos_voo[(o, d)] = dist / vel_media_c97
tempos_voo_min[(o, d)] = int(round((dist / vel_media_c97) * 60))
# Demanda fixa D_{i,j}
D = {(o, d): 0 for o in aeroportos_malha for d in aeroportos_malha}
@@ -338,7 +338,7 @@ N = pulp.LpVariable("N", lowBound=0, cat='Integer')
# Função Objetivo: Minimizar N
# Para desempatar soluções com mesmo N, damos um pequeno peso pro tempo de reposicionamento vazio (para pegar o roteamento mais curto também)
prob2 += N + 0.0001 * pulp.lpSum([Y[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
prob2 += N + 0.0001 * pulp.lpSum([Y[(i, j)] * tempos_voo_min[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
# Restrição de conservação de fluxo para cada aeroporto (Chegadas == Partidas)
for no in aeroportos_malha:
@@ -346,9 +346,9 @@ for no in aeroportos_malha:
partidas = pulp.lpSum([D[(no, j)] + Y[(no, j)] for j in aeroportos_malha])
prob2 += chegadas == partidas
# Restrição de tempo (O tempo total de voo da malha não pode exceder N * 24h)
tempo_total_voo = pulp.lpSum([(D[(i, j)] + Y[(i, j)]) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
prob2 += tempo_total_voo <= N * 24.0
# Restrição de tempo (O tempo total de voo da malha não pode exceder N * 1440 min)
tempo_total_voo = pulp.lpSum([(D[(i, j)] + Y[(i, j)]) * tempos_voo_min[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
prob2 += tempo_total_voo <= N * 1440
# Solução
prob2.solve(pulp.PULP_CBC_CMD(msg=False))
@@ -357,17 +357,17 @@ print("\\n== RESULTADO DO MODELO 2 ==")
print(f"Status: {pulp.LpStatus[prob2.status]}")
print(f"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N.varValue)}")
tempo_gasto = sum([(D[(i, j)] + Y[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
print(f"Tempo total de voo diário (incluindo reposicionamento vazio): {tempo_gasto:.2f} horas")
tempo_gasto = sum([(D[(i, j)] + int(Y[(i, j)].varValue)) * tempos_voo_min[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
print(f"Tempo total de voo diário (incluindo reposicionamento vazio): {int(tempo_gasto)} min ({tempo_gasto/60:.2f} h)")
if N.varValue > 0:
print(f"Taxa de ocupação da frota (Tempo Voo / Tempo Disponível): {(tempo_gasto / (N.varValue * 24)) * 100:.1f}%\\n")
print(f"Taxa de ocupação da frota (Tempo Voo / Tempo Disponível): {(tempo_gasto / (N.varValue * 1440)) * 100:.1f}%\\n")
print("Voos de Reposicionamento Vazio Gerados (Y) para fechar o ciclo diário:")
gerou_vazio = False
for i in aeroportos_malha:
for j in aeroportos_malha:
if Y[(i, j)].varValue > 0:
print(f" -> De {i} para {j}: {int(Y[(i, j)].varValue)} voo(s) vazio(s) | Tempo de voo: {tempos_voo[(i,j)]*Y[(i,j)].varValue:.1f}h")
print(f" -> De {i} para {j}: {int(Y[(i, j)].varValue)} voo(s) vazio(s) | Tempo de voo: {tempos_voo_min[(i,j)]*int(Y[(i,j)].varValue)} min")
gerou_vazio = True
if not gerou_vazio:
print(" Nenhum voo de reposicionamento vazio foi necessário! A malha já é circular/balanceada.")
@@ -391,7 +391,7 @@ for i in aeroportos_malha:
D_copy[(i, j)] -= 1
else:
tipo = 'Vazio (Reposicionamento)'
voos_para_fazer.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]})
voos_para_fazer.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo_min[(i, j)]})
# 2. Simular a alocação sequencial de horários
tabela_horarios = []
@@ -401,7 +401,7 @@ while voos_para_fazer:
voo_atual = voos_para_fazer.pop(0)
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
tempo_delta = datetime.timedelta(hours=voo_atual['tempo'])
tempo_delta = datetime.timedelta(minutes=int(voo_atual['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios.append([
@@ -411,7 +411,7 @@ while voos_para_fazer:
voo_atual['tipo'],
hora_atual.strftime('%H:%M'),
hora_chegada.strftime('%H:%M'),
f"{voo_atual['tempo']:.2f} h"
f"{voo_atual['tempo']} min"
])
hora_atual = hora_chegada
@@ -422,12 +422,12 @@ while voos_para_fazer:
prox_voo = None
for idx, v in enumerate(voos_para_fazer):
if v['origem'] == local_atual:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(hours=v['tempo'])).total_seconds() <= 24 * 3600:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(minutes=int(v['tempo']))).total_seconds() <= 1440 * 60:
prox_voo = voos_para_fazer.pop(idx)
break
if prox_voo:
tempo_delta = datetime.timedelta(hours=prox_voo['tempo'])
tempo_delta = datetime.timedelta(minutes=int(prox_voo['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios.append([
f"Aeronave {aeronave_id}",
@@ -436,7 +436,7 @@ while voos_para_fazer:
prox_voo['tipo'],
hora_atual.strftime('%H:%M'),
hora_chegada.strftime('%H:%M'),
f"{prox_voo['tempo']:.2f} h"
f"{prox_voo['tempo']} min"
])
hora_atual = hora_chegada
local_atual = prox_voo['destino']
@@ -465,10 +465,10 @@ prob3 = pulp.LpProblem("Minimizar_Aeronaves_TAT", pulp.LpMinimize)
Y3 = pulp.LpVariable.dicts("Y3", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')
N3 = pulp.LpVariable("N3", lowBound=0, cat='Integer')
TAT_horas = 40.0 / 60.0
TAT_min = 40
# Função Objetivo
prob3 += N3 + 0.0001 * pulp.lpSum([Y3[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
prob3 += N3 + 0.0001 * pulp.lpSum([Y3[(i, j)] * tempos_voo_min[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
# Restrição de fluxo
for no in aeroportos_malha:
@@ -478,8 +478,8 @@ for no in aeroportos_malha:
# Restrição de tempo com TAT
# Cada voo (D ou Y) consome tempo de voo + TAT
tempo_total_operacional = pulp.lpSum([(D[(i, j)] + Y3[(i, j)]) * (tempos_voo[(i, j)] + TAT_horas) for i in aeroportos_malha for j in aeroportos_malha])
prob3 += tempo_total_operacional <= N3 * 24.0
tempo_total_operacional = pulp.lpSum([(D[(i, j)] + Y3[(i, j)]) * (tempos_voo_min[(i, j)] + TAT_min) for i in aeroportos_malha for j in aeroportos_malha])
prob3 += tempo_total_operacional <= N3 * 1440
# Solução
prob3.solve(pulp.PULP_CBC_CMD(msg=False))
@@ -488,15 +488,15 @@ print("\\n== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==")
print(f"Status: {pulp.LpStatus[prob3.status]}")
print(f"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N3.varValue)}")
tempo_voo_puro = sum([(D[(i, j)] + Y3[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
total_voos_realizados = sum([(D[(i, j)] + Y3[(i, j)].varValue) for i in aeroportos_malha for j in aeroportos_malha])
tempo_solo_total = total_voos_realizados * TAT_horas
tempo_voo_puro = sum([(D[(i, j)] + int(Y3[(i, j)].varValue)) * tempos_voo_min[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
total_voos_realizados = sum([(D[(i, j)] + int(Y3[(i, j)].varValue)) for i in aeroportos_malha for j in aeroportos_malha])
tempo_solo_total = total_voos_realizados * TAT_min
print(f"Tempo total de voo puro: {tempo_voo_puro:.2f} horas")
print(f"Tempo total gasto em solo (TAT): {tempo_solo_total:.2f} horas")
print(f"Tempo total operacional comprometido: {tempo_voo_puro + tempo_solo_total:.2f} horas")
print(f"Tempo total de voo puro: {tempo_voo_puro} min ({tempo_voo_puro/60:.2f} h)")
print(f"Tempo total gasto em solo (TAT): {tempo_solo_total} min ({tempo_solo_total/60:.2f} h)")
print(f"Tempo total operacional comprometido: {tempo_voo_puro + tempo_solo_total} min ({(tempo_voo_puro + tempo_solo_total)/60:.2f} h)")
if N3.varValue > 0:
print(f"Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): {((tempo_voo_puro + tempo_solo_total) / (N3.varValue * 24)) * 100:.1f}%\\n")
print(f"Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): {((tempo_voo_puro + tempo_solo_total) / (N3.varValue * 1440)) * 100:.1f}%\\n")
# GERAR TABELA DE HORÁRIOS COM TABULATE E DATETIME
voos_para_fazer3 = []
@@ -511,7 +511,7 @@ for i in aeroportos_malha:
D_copy3[(i, j)] -= 1
else:
tipo = 'Vazio (Reposicionamento)'
voos_para_fazer3.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]})
voos_para_fazer3.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo_min[(i, j)]})
tabela_horarios3 = []
aeronave_id3 = 1
@@ -520,7 +520,7 @@ while voos_para_fazer3:
voo_atual = voos_para_fazer3.pop(0)
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
tempo_delta = datetime.timedelta(hours=voo_atual['tempo'])
tempo_delta = datetime.timedelta(minutes=int(voo_atual['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios3.append([
@@ -530,7 +530,7 @@ while voos_para_fazer3:
voo_atual['tipo'],
hora_atual.strftime('%H:%M'),
hora_chegada.strftime('%H:%M'),
f"{voo_atual['tempo']:.2f} h"
f"{voo_atual['tempo']} min"
])
# Próxima partida só depois do TAT de 40 min
@@ -541,12 +541,12 @@ while voos_para_fazer3:
prox_voo = None
for idx, v in enumerate(voos_para_fazer3):
if v['origem'] == local_atual:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(hours=v['tempo'])).total_seconds() <= 24 * 3600:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(minutes=int(v['tempo']))).total_seconds() <= 1440 * 60:
prox_voo = voos_para_fazer3.pop(idx)
break
if prox_voo:
tempo_delta = datetime.timedelta(hours=prox_voo['tempo'])
tempo_delta = datetime.timedelta(minutes=int(prox_voo['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios3.append([
f"Aeronave {aeronave_id3}",
@@ -555,7 +555,7 @@ while voos_para_fazer3:
prox_voo['tipo'],
hora_atual.strftime('%H:%M'),
hora_chegada.strftime('%H:%M'),
f"{prox_voo['tempo']:.2f} h"
f"{prox_voo['tempo']} min"
])
hora_atual = hora_chegada + datetime.timedelta(minutes=40)
local_atual = prox_voo['destino']