feat: adiciona Modelo 2 para minimização do número absoluto de aeronaves
This commit is contained in:
101
create_nb.py
101
create_nb.py
@@ -272,6 +272,107 @@ for r in rotas_lista:
|
|||||||
print(f"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.")
|
print(f"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.")
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
|
# Cell 11: Markdown Modelo 2
|
||||||
|
cells.append(nbf.v4.new_markdown_cell("""## 5. Modelo 2: Minimização de Aeronaves
|
||||||
|
Neste modelo, o objetivo é encontrar o **número mínimo absoluto de aeronaves** necessário para operar a malha diária estabelecida (os top trechos).
|
||||||
|
Considerações solicitadas:
|
||||||
|
- O tempo de voo é estimado usando a velocidade média real do C-97 em 2025.
|
||||||
|
- As aeronaves podem voar até 24 horas por dia (não consideraremos o tempo de solo no chão nesta versão).
|
||||||
|
- Se houver desbalanceamento de malha (mais voos saindo de A do que chegando em A), o modelo criará voos de reposicionamento vazios.
|
||||||
|
"""))
|
||||||
|
|
||||||
|
# Cell 12: Code Modelo 2
|
||||||
|
cells.append(nbf.v4.new_code_cell("""# 1. Calcular a velocidade média do C-97 em 2025
|
||||||
|
# Converte a string 'HH:MM:SS' para timedelta e extrai horas
|
||||||
|
df_c97_spd = df_c97.copy()
|
||||||
|
df_c97_spd['Tempo_Horas'] = pd.to_timedelta(df_c97_spd['Tempo de Voo']).dt.total_seconds() / 3600
|
||||||
|
|
||||||
|
def get_dist_row(row):
|
||||||
|
try:
|
||||||
|
lat1, lon1, alt1 = df_aeroportos.loc[row['Localidade Decolagem'], ['lat', 'lon', 'elevation']]
|
||||||
|
lat2, lon2, alt2 = df_aeroportos.loc[row['Localidade Pouso'], ['lat', 'lon', 'elevation']]
|
||||||
|
return pm.geodetic2aer(lat1, lon1, alt1, lat2, lon2, alt2)[2] / 1000.0
|
||||||
|
except:
|
||||||
|
return np.nan
|
||||||
|
|
||||||
|
df_c97_spd['Dist_km'] = df_c97_spd.apply(get_dist_row, axis=1)
|
||||||
|
|
||||||
|
# Filtra voos válidos para calcular a velocidade média
|
||||||
|
df_valid = df_c97_spd[(df_c97_spd['Tempo_Horas'] > 0) & (df_c97_spd['Dist_km'] > 0)].copy()
|
||||||
|
df_valid['Velocidade'] = df_valid['Dist_km'] / df_valid['Tempo_Horas']
|
||||||
|
vel_media_c97 = df_valid['Velocidade'].mean()
|
||||||
|
print(f"Velocidade média calculada para o C-97: {vel_media_c97:.2f} km/h")
|
||||||
|
|
||||||
|
# 2. Configurar os nós (aeroportos) e demandas da malha das rotas top
|
||||||
|
aeroportos_malha = set()
|
||||||
|
for r in rotas_lista:
|
||||||
|
o, d = r.split('-')
|
||||||
|
aeroportos_malha.add(o)
|
||||||
|
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 = {}
|
||||||
|
for o in aeroportos_malha:
|
||||||
|
for d in aeroportos_malha:
|
||||||
|
if o == d:
|
||||||
|
tempos_voo[(o, d)] = 0
|
||||||
|
else:
|
||||||
|
dist = calc_distancia(o, d)
|
||||||
|
tempos_voo[(o, d)] = dist / vel_media_c97
|
||||||
|
|
||||||
|
# Demanda fixa D_{i,j}
|
||||||
|
D = {(o, d): 0 for o in aeroportos_malha for d in aeroportos_malha}
|
||||||
|
for r in rotas_lista:
|
||||||
|
o, d = r.split('-')
|
||||||
|
D[(o, d)] = voos_requeridos[r]
|
||||||
|
|
||||||
|
# 3. Modelagem Matemática (Pulp)
|
||||||
|
prob2 = pulp.LpProblem("Minimizar_Aeronaves", pulp.LpMinimize)
|
||||||
|
|
||||||
|
# Variáveis de reposicionamento vazio Y_{i,j}
|
||||||
|
Y = pulp.LpVariable.dicts("Y", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')
|
||||||
|
|
||||||
|
# Variável de número de aeronaves N
|
||||||
|
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])
|
||||||
|
|
||||||
|
# Restrição de conservação de fluxo para cada aeroporto (Chegadas == Partidas)
|
||||||
|
for no in aeroportos_malha:
|
||||||
|
chegadas = pulp.lpSum([D[(i, no)] + Y[(i, no)] for i 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
|
||||||
|
|
||||||
|
# Solução
|
||||||
|
prob2.solve(pulp.PULP_CBC_CMD(msg=False))
|
||||||
|
|
||||||
|
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")
|
||||||
|
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("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")
|
||||||
|
gerou_vazio = True
|
||||||
|
if not gerou_vazio:
|
||||||
|
print(" Nenhum voo de reposicionamento vazio foi necessário! A malha já é circular/balanceada.")
|
||||||
|
"""))
|
||||||
|
|
||||||
nb.cells = cells
|
nb.cells = cells
|
||||||
|
|
||||||
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
||||||
|
|||||||
207
modelos.ipynb
207
modelos.ipynb
@@ -2,7 +2,7 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "3057dead",
|
"id": "07d7e46d",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Alocação de Frotas (Fleet Scheduling) - Aeronaves C-97\n",
|
"# Alocação de Frotas (Fleet Scheduling) - Aeronaves C-97\n",
|
||||||
@@ -21,13 +21,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 1,
|
||||||
"id": "8c9a45e2",
|
"id": "3f9fa4c6",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:32:32.992543Z",
|
"iopub.execute_input": "2026-06-07T23:37:16.312196Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:32:32.992366Z",
|
"iopub.status.busy": "2026-06-07T23:37:16.311487Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:32:33.601354Z",
|
"iopub.status.idle": "2026-06-07T23:37:16.933888Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:32:33.600709Z"
|
"shell.execute_reply": "2026-06-07T23:37:16.933301Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "cfe85206",
|
"id": "6054dba5",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 1. Leitura e Limpeza dos Dados\n",
|
"## 1. Leitura e Limpeza dos Dados\n",
|
||||||
@@ -63,13 +63,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 2,
|
||||||
"id": "32d7c973",
|
"id": "da258d7f",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:32:33.603095Z",
|
"iopub.execute_input": "2026-06-07T23:37:16.935502Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:32:33.602885Z",
|
"iopub.status.busy": "2026-06-07T23:37:16.935296Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:32:33.976197Z",
|
"iopub.status.idle": "2026-06-07T23:37:17.297220Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:32:33.975310Z"
|
"shell.execute_reply": "2026-06-07T23:37:17.296482Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "e1b1fe07",
|
"id": "2424db2d",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 2. Análise dos Esquadrões e Matrículas (Aeronaves)\n",
|
"## 2. Análise dos Esquadrões e Matrículas (Aeronaves)\n",
|
||||||
@@ -111,13 +111,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": 3,
|
||||||
"id": "5016861c",
|
"id": "8b308e12",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:32:33.977763Z",
|
"iopub.execute_input": "2026-06-07T23:37:17.298752Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:32:33.977632Z",
|
"iopub.status.busy": "2026-06-07T23:37:17.298630Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:32:33.989318Z",
|
"iopub.status.idle": "2026-06-07T23:37:17.310776Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:32:33.988806Z"
|
"shell.execute_reply": "2026-06-07T23:37:17.310206Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "2fe94947",
|
"id": "4909bfef",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 3. Análise Estatística das Demandas Diárias\n",
|
"## 3. Análise Estatística das Demandas Diárias\n",
|
||||||
@@ -187,13 +187,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 4,
|
||||||
"id": "73a0064f",
|
"id": "b38abd07",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:32:33.992417Z",
|
"iopub.execute_input": "2026-06-07T23:37:17.312196Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:32:33.992279Z",
|
"iopub.status.busy": "2026-06-07T23:37:17.312078Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:32:34.310958Z",
|
"iopub.status.idle": "2026-06-07T23:37:17.621631Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:32:34.310336Z"
|
"shell.execute_reply": "2026-06-07T23:37:17.620923Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "c81003e5",
|
"id": "6bab0068",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Diagrama de Rede dos Trechos\n",
|
"## Diagrama de Rede dos Trechos\n",
|
||||||
@@ -434,13 +434,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 5,
|
"execution_count": 5,
|
||||||
"id": "bb72e82d",
|
"id": "6198127c",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:32:34.312383Z",
|
"iopub.execute_input": "2026-06-07T23:37:17.623083Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:32:34.312250Z",
|
"iopub.status.busy": "2026-06-07T23:37:17.622958Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:32:34.430777Z",
|
"iopub.status.idle": "2026-06-07T23:37:17.740324Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:32:34.430189Z"
|
"shell.execute_reply": "2026-06-07T23:37:17.739229Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -497,7 +497,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "e5daac1f",
|
"id": "35633067",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 4. Fleet Assignment Model (FAM) - Nível Brasil\n",
|
"## 4. Fleet Assignment Model (FAM) - Nível Brasil\n",
|
||||||
@@ -514,13 +514,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 6,
|
"execution_count": 6,
|
||||||
"id": "26694904",
|
"id": "c9a2018d",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:32:34.432272Z",
|
"iopub.execute_input": "2026-06-07T23:37:17.742588Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:32:34.432145Z",
|
"iopub.status.busy": "2026-06-07T23:37:17.742459Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:32:34.456734Z",
|
"iopub.status.idle": "2026-06-07T23:37:17.764890Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:32:34.456266Z"
|
"shell.execute_reply": "2026-06-07T23:37:17.764350Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -606,6 +606,141 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"print(f\"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.\")\n"
|
"print(f\"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.\")\n"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "666ba740",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## 5. Modelo 2: Minimização de Aeronaves\n",
|
||||||
|
"Neste modelo, o objetivo é encontrar o **número mínimo absoluto de aeronaves** necessário para operar a malha diária estabelecida (os top trechos).\n",
|
||||||
|
"Considerações solicitadas:\n",
|
||||||
|
"- O tempo de voo é estimado usando a velocidade média real do C-97 em 2025.\n",
|
||||||
|
"- As aeronaves podem voar até 24 horas por dia (não consideraremos o tempo de solo no chão nesta versão).\n",
|
||||||
|
"- Se houver desbalanceamento de malha (mais voos saindo de A do que chegando em A), o modelo criará voos de reposicionamento vazios.\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
|
"id": "7b9b8019",
|
||||||
|
"metadata": {
|
||||||
|
"execution": {
|
||||||
|
"iopub.execute_input": "2026-06-07T23:37:17.766458Z",
|
||||||
|
"iopub.status.busy": "2026-06-07T23:37:17.766338Z",
|
||||||
|
"iopub.status.idle": "2026-06-07T23:37:19.072202Z",
|
||||||
|
"shell.execute_reply": "2026-06-07T23:37:19.071522Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Velocidade média calculada para o C-97: 391.76 km/h\n",
|
||||||
|
"\n",
|
||||||
|
"== RESULTADO DO MODELO 2 ==\n",
|
||||||
|
"Status: Optimal\n",
|
||||||
|
"Número mínimo absoluto de aeronaves C-97 necessárias: 1\n",
|
||||||
|
"Tempo total de voo diário (incluindo reposicionamento vazio): 16.70 horas\n",
|
||||||
|
"Taxa de ocupação da frota (Tempo Voo / Tempo Disponível): 69.6%\n",
|
||||||
|
"\n",
|
||||||
|
"Voos de Reposicionamento Vazio Gerados (Y) para fechar o ciclo diário:\n",
|
||||||
|
" -> De SBAN para SBSJ: 1 voo(s) vazio(s) | Tempo de voo: 2.1h\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# 1. Calcular a velocidade média do C-97 em 2025\n",
|
||||||
|
"# Converte a string 'HH:MM:SS' para timedelta e extrai horas\n",
|
||||||
|
"df_c97_spd = df_c97.copy()\n",
|
||||||
|
"df_c97_spd['Tempo_Horas'] = pd.to_timedelta(df_c97_spd['Tempo de Voo']).dt.total_seconds() / 3600\n",
|
||||||
|
"\n",
|
||||||
|
"def get_dist_row(row):\n",
|
||||||
|
" try:\n",
|
||||||
|
" lat1, lon1, alt1 = df_aeroportos.loc[row['Localidade Decolagem'], ['lat', 'lon', 'elevation']]\n",
|
||||||
|
" lat2, lon2, alt2 = df_aeroportos.loc[row['Localidade Pouso'], ['lat', 'lon', 'elevation']]\n",
|
||||||
|
" return pm.geodetic2aer(lat1, lon1, alt1, lat2, lon2, alt2)[2] / 1000.0\n",
|
||||||
|
" except:\n",
|
||||||
|
" return np.nan\n",
|
||||||
|
"\n",
|
||||||
|
"df_c97_spd['Dist_km'] = df_c97_spd.apply(get_dist_row, axis=1)\n",
|
||||||
|
"\n",
|
||||||
|
"# Filtra voos válidos para calcular a velocidade média\n",
|
||||||
|
"df_valid = df_c97_spd[(df_c97_spd['Tempo_Horas'] > 0) & (df_c97_spd['Dist_km'] > 0)].copy()\n",
|
||||||
|
"df_valid['Velocidade'] = df_valid['Dist_km'] / df_valid['Tempo_Horas']\n",
|
||||||
|
"vel_media_c97 = df_valid['Velocidade'].mean()\n",
|
||||||
|
"print(f\"Velocidade média calculada para o C-97: {vel_media_c97:.2f} km/h\")\n",
|
||||||
|
"\n",
|
||||||
|
"# 2. Configurar os nós (aeroportos) e demandas da malha das rotas top\n",
|
||||||
|
"aeroportos_malha = set()\n",
|
||||||
|
"for r in rotas_lista:\n",
|
||||||
|
" o, d = r.split('-')\n",
|
||||||
|
" aeroportos_malha.add(o)\n",
|
||||||
|
" aeroportos_malha.add(d)\n",
|
||||||
|
"aeroportos_malha = list(aeroportos_malha)\n",
|
||||||
|
"\n",
|
||||||
|
"# Matriz de tempo de voo entre todos os nós da malha (para reposicionamento)\n",
|
||||||
|
"tempos_voo = {}\n",
|
||||||
|
"for o in aeroportos_malha:\n",
|
||||||
|
" for d in aeroportos_malha:\n",
|
||||||
|
" if o == d:\n",
|
||||||
|
" tempos_voo[(o, d)] = 0\n",
|
||||||
|
" else:\n",
|
||||||
|
" dist = calc_distancia(o, d)\n",
|
||||||
|
" tempos_voo[(o, d)] = dist / vel_media_c97\n",
|
||||||
|
"\n",
|
||||||
|
"# Demanda fixa D_{i,j}\n",
|
||||||
|
"D = {(o, d): 0 for o in aeroportos_malha for d in aeroportos_malha}\n",
|
||||||
|
"for r in rotas_lista:\n",
|
||||||
|
" o, d = r.split('-')\n",
|
||||||
|
" D[(o, d)] = voos_requeridos[r]\n",
|
||||||
|
"\n",
|
||||||
|
"# 3. Modelagem Matemática (Pulp)\n",
|
||||||
|
"prob2 = pulp.LpProblem(\"Minimizar_Aeronaves\", pulp.LpMinimize)\n",
|
||||||
|
"\n",
|
||||||
|
"# Variáveis de reposicionamento vazio Y_{i,j}\n",
|
||||||
|
"Y = pulp.LpVariable.dicts(\"Y\", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')\n",
|
||||||
|
"\n",
|
||||||
|
"# Variável de número de aeronaves N\n",
|
||||||
|
"N = pulp.LpVariable(\"N\", lowBound=0, cat='Integer')\n",
|
||||||
|
"\n",
|
||||||
|
"# Função Objetivo: Minimizar N\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)\n",
|
||||||
|
"prob2 += N + 0.0001 * pulp.lpSum([Y[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])\n",
|
||||||
|
"\n",
|
||||||
|
"# Restrição de conservação de fluxo para cada aeroporto (Chegadas == Partidas)\n",
|
||||||
|
"for no in aeroportos_malha:\n",
|
||||||
|
" chegadas = pulp.lpSum([D[(i, no)] + Y[(i, no)] for i in aeroportos_malha])\n",
|
||||||
|
" partidas = pulp.lpSum([D[(no, j)] + Y[(no, j)] for j in aeroportos_malha])\n",
|
||||||
|
" prob2 += chegadas == partidas\n",
|
||||||
|
"\n",
|
||||||
|
"# Restrição de tempo (O tempo total de voo da malha não pode exceder N * 24h)\n",
|
||||||
|
"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])\n",
|
||||||
|
"prob2 += tempo_total_voo <= N * 24.0\n",
|
||||||
|
"\n",
|
||||||
|
"# Solução\n",
|
||||||
|
"prob2.solve(pulp.PULP_CBC_CMD(msg=False))\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"\\n== RESULTADO DO MODELO 2 ==\")\n",
|
||||||
|
"print(f\"Status: {pulp.LpStatus[prob2.status]}\")\n",
|
||||||
|
"print(f\"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N.varValue)}\")\n",
|
||||||
|
"\n",
|
||||||
|
"tempo_gasto = sum([(D[(i, j)] + Y[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])\n",
|
||||||
|
"print(f\"Tempo total de voo diário (incluindo reposicionamento vazio): {tempo_gasto:.2f} horas\")\n",
|
||||||
|
"if N.varValue > 0:\n",
|
||||||
|
" print(f\"Taxa de ocupação da frota (Tempo Voo / Tempo Disponível): {(tempo_gasto / (N.varValue * 24)) * 100:.1f}%\\n\")\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"Voos de Reposicionamento Vazio Gerados (Y) para fechar o ciclo diário:\")\n",
|
||||||
|
"gerou_vazio = False\n",
|
||||||
|
"for i in aeroportos_malha:\n",
|
||||||
|
" for j in aeroportos_malha:\n",
|
||||||
|
" if Y[(i, j)].varValue > 0:\n",
|
||||||
|
" 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\")\n",
|
||||||
|
" gerou_vazio = True\n",
|
||||||
|
"if not gerou_vazio:\n",
|
||||||
|
" print(\" Nenhum voo de reposicionamento vazio foi necessário! A malha já é circular/balanceada.\")\n"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
Reference in New Issue
Block a user