refactor: simplifica Fleet Assignment para nível Brasil sem restrição de esquadrões (Modelo 1)

This commit is contained in:
2026-06-07 20:32:35 -03:00
parent 15bcd7e962
commit edf6ef9d8f
2 changed files with 104 additions and 134 deletions

View File

@@ -202,22 +202,22 @@ plt.show()
""")) """))
# Cell 9: Markdown FAM # Cell 9: Markdown FAM
cells.append(nbf.v4.new_markdown_cell("""## 4. Fleet Assignment Model (FAM) cells.append(nbf.v4.new_markdown_cell("""## 4. Fleet Assignment Model (FAM) - Nível Brasil
Conforme o livro base *Airline Operations and Scheduling*, o problema de alocação de frotas busca atribuir os tipos de aeronaves às pernas de voo visando minimizar o custo e as perdas de receita (spill). Como temos apenas um tipo (C-97), nosso problema adapta-se a uma **alocação de esquadrões (e suas respectivas bases)** aos voos requeridos para minimizar o custo total de operação, que é proporcional à distância voada. Conforme o MODELO 1 solicitado (aeronaves podem pernoitar em qualquer hub), não há restrição de base ou esquadrão específico. Toda a frota de C-97 opera a nível nacional.
**Modelo Matemático:** **Modelo Matemático:**
- **Variáveis de decisão:** $X_{s, r}$ (Quantidade de voos operados pelo esquadrão $s$ no trecho $r$) - **Variáveis de decisão:** $X_r$ (Quantidade de voos alocados no trecho $r$)
- **Função Objetivo:** Minimizar a distância total (Distância de posicionamento da base + Distância do trecho + Retorno para a base). - **Função Objetivo:** Minimizar a distância total da operação (Distância do trecho $\\times$ voos no trecho). Como as aeronaves não precisam retornar a uma base fixa, desconsideramos voos de posicionamento.
- **Restrições:** - **Restrições:**
1. O número de voos alocados para o trecho deve suprir a demanda média (Média PAX / Capacidade do C-97). 1. O número de voos alocados para o trecho deve suprir a demanda diária operada daquela rota.
2. O total de voos atribuídos a um esquadrão não pode exceder o número de aeronaves disponíveis naquele esquadrão no dia. 2. O total de voos atribuídos no dia não pode exceder o total da frota de C-97 disponível (14 aeronaves).
""")) """))
# Cell 10: Code FAM # Cell 10: Code FAM
cells.append(nbf.v4.new_code_cell("""# Preparação dos dados para o modelo cells.append(nbf.v4.new_code_cell("""# Preparação dos dados para o modelo
rotas_lista = top_trechos['Trecho'].tolist() rotas_lista = top_trechos['Trecho'].tolist()
# Dicionário de distâncias # Dicionário de distâncias apenas dos trechos diretos
def calc_distancia(icao1, icao2): def calc_distancia(icao1, icao2):
try: try:
lat1, lon1, alt1 = df_aeroportos.loc[icao1, ['lat', 'lon', 'elevation']] lat1, lon1, alt1 = df_aeroportos.loc[icao1, ['lat', 'lon', 'elevation']]
@@ -230,52 +230,46 @@ def calc_distancia(icao1, icao2):
distancias_voo = {} distancias_voo = {}
for r in rotas_lista: for r in rotas_lista:
origem, destino = r.split('-') origem, destino = r.split('-')
dist_trecho = calc_distancia(origem, destino) distancias_voo[r] = calc_distancia(origem, destino)
for esq, base in bases_esquadroes.items():
dist_ida = calc_distancia(base, origem) if base != origem else 0
dist_volta = calc_distancia(destino, base) if base != destino else 0
distancias_voo[(esq, r)] = dist_ida + dist_trecho + dist_volta
# Demanda de voos por rota (Média diária) # Demanda de voos por rota
voos_requeridos = {} voos_requeridos = {}
for _, row in top_trechos.iterrows(): for _, row in top_trechos.iterrows():
# Arredondamento para cima da (demanda / capacidade)
voos = math.ceil(row['Media_PAX_Dias_Operados'] / CAPACIDADE_C97) voos = math.ceil(row['Media_PAX_Dias_Operados'] / CAPACIDADE_C97)
# Pelo menos 1 voo para suprir a demanda da rota selecionada
voos_requeridos[row['Trecho']] = max(1, voos) voos_requeridos[row['Trecho']] = max(1, voos)
# MODELAGEM COM PULP # MODELAGEM COM PULP
prob = pulp.LpProblem("Fleet_Assignment_C97", pulp.LpMinimize) prob = pulp.LpProblem("Fleet_Assignment_C97_Brasil", pulp.LpMinimize)
# Variáveis # Variáveis (número de voos em cada rota)
X = pulp.LpVariable.dicts("X", [(esq, r) for esq in aeronaves_por_esquadrao.keys() for r in rotas_lista], lowBound=0, cat='Integer') X = pulp.LpVariable.dicts("X", rotas_lista, lowBound=0, cat='Integer')
# Função Objetivo # Função Objetivo: Minimizar distância voada nos trechos
prob += pulp.lpSum([distancias_voo[(esq, r)] * X[(esq, r)] for esq in aeronaves_por_esquadrao.keys() for r in rotas_lista]) prob += pulp.lpSum([distancias_voo[r] * X[r] for r in rotas_lista])
# Restrição 1: Suprir a demanda da rota # Restrição 1: Suprir a demanda
for r in rotas_lista: for r in rotas_lista:
prob += pulp.lpSum([X[(esq, r)] for esq in aeronaves_por_esquadrao.keys()]) >= voos_requeridos[r] prob += X[r] >= voos_requeridos[r]
# Restrição 2: Limite de aeronaves por esquadrão # Restrição 2: Limite global de frota (nível Brasil)
for esq in aeronaves_por_esquadrao.keys(): prob += pulp.lpSum([X[r] for r in rotas_lista]) <= total_aeronaves
prob += pulp.lpSum([X[(esq, r)] for r in rotas_lista]) <= aeronaves_por_esquadrao[esq]
# Solução # Solução
prob.solve(pulp.PULP_CBC_CMD(msg=False)) prob.solve(pulp.PULP_CBC_CMD(msg=False))
# Exibição dos resultados # Exibição dos resultados
print("== RESULTADO DO FLEET ASSIGNMENT ==") print("== RESULTADO DO FLEET ASSIGNMENT (NÍVEL BRASIL) ==")
print(f"Status: {pulp.LpStatus[prob.status]}") print(f"Status: {pulp.LpStatus[prob.status]}")
print(f"Distância Total Minimizada: {pulp.value(prob.objective):.2f} km\\n") print(f"Distância Total da Operação: {pulp.value(prob.objective):.2f} km\\n")
print("Alocações (Voos Diários):") print("Alocações (Voos Diários Globais):")
voos_totais = 0
for r in rotas_lista: for r in rotas_lista:
print(f"\\nTrecho: {r} (Voos necessários: {voos_requeridos[r]})") qtd = int(X[r].varValue)
for esq in aeronaves_por_esquadrao.keys(): voos_totais += qtd
qtd = int(X[(esq, r)].varValue) print(f"Trecho: {r:10} | Demanda Média PAX: {voos_requeridos[r]*CAPACIDADE_C97:3.0f} | Alocados: {qtd} voo(s)")
if qtd > 0:
print(f" -> {esq} (Base {bases_esquadroes[esq]}): {qtd} voo(s)") print(f"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.")
""")) """))
nb.cells = cells nb.cells = cells

View File

@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "6be31f50", "id": "3057dead",
"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": "f4b8c44d", "id": "8c9a45e2",
"metadata": { "metadata": {
"execution": { "execution": {
"iopub.execute_input": "2026-06-07T23:27:29.153892Z", "iopub.execute_input": "2026-06-07T23:32:32.992543Z",
"iopub.status.busy": "2026-06-07T23:27:29.153192Z", "iopub.status.busy": "2026-06-07T23:32:32.992366Z",
"iopub.status.idle": "2026-06-07T23:27:29.811580Z", "iopub.status.idle": "2026-06-07T23:32:33.601354Z",
"shell.execute_reply": "2026-06-07T23:27:29.810502Z" "shell.execute_reply": "2026-06-07T23:32:33.600709Z"
} }
}, },
"outputs": [], "outputs": [],
@@ -53,7 +53,7 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "4a3f8284", "id": "cfe85206",
"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": "43261510", "id": "32d7c973",
"metadata": { "metadata": {
"execution": { "execution": {
"iopub.execute_input": "2026-06-07T23:27:29.813511Z", "iopub.execute_input": "2026-06-07T23:32:33.603095Z",
"iopub.status.busy": "2026-06-07T23:27:29.813233Z", "iopub.status.busy": "2026-06-07T23:32:33.602885Z",
"iopub.status.idle": "2026-06-07T23:27:30.183686Z", "iopub.status.idle": "2026-06-07T23:32:33.976197Z",
"shell.execute_reply": "2026-06-07T23:27:30.182986Z" "shell.execute_reply": "2026-06-07T23:32:33.975310Z"
} }
}, },
"outputs": [], "outputs": [],
@@ -101,7 +101,7 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "bffd4e0c", "id": "e1b1fe07",
"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": "40061c45", "id": "5016861c",
"metadata": { "metadata": {
"execution": { "execution": {
"iopub.execute_input": "2026-06-07T23:27:30.185144Z", "iopub.execute_input": "2026-06-07T23:32:33.977763Z",
"iopub.status.busy": "2026-06-07T23:27:30.185022Z", "iopub.status.busy": "2026-06-07T23:32:33.977632Z",
"iopub.status.idle": "2026-06-07T23:27:30.196292Z", "iopub.status.idle": "2026-06-07T23:32:33.989318Z",
"shell.execute_reply": "2026-06-07T23:27:30.195756Z" "shell.execute_reply": "2026-06-07T23:32:33.988806Z"
} }
}, },
"outputs": [ "outputs": [
@@ -176,7 +176,7 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "94ec1687", "id": "2fe94947",
"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": "9e2bb9ab", "id": "73a0064f",
"metadata": { "metadata": {
"execution": { "execution": {
"iopub.execute_input": "2026-06-07T23:27:30.197674Z", "iopub.execute_input": "2026-06-07T23:32:33.992417Z",
"iopub.status.busy": "2026-06-07T23:27:30.197555Z", "iopub.status.busy": "2026-06-07T23:32:33.992279Z",
"iopub.status.idle": "2026-06-07T23:27:30.501774Z", "iopub.status.idle": "2026-06-07T23:32:34.310958Z",
"shell.execute_reply": "2026-06-07T23:27:30.501194Z" "shell.execute_reply": "2026-06-07T23:32:34.310336Z"
} }
}, },
"outputs": [ "outputs": [
@@ -424,7 +424,7 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "d6f1f668", "id": "c81003e5",
"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": "4929bba6", "id": "bb72e82d",
"metadata": { "metadata": {
"execution": { "execution": {
"iopub.execute_input": "2026-06-07T23:27:30.503463Z", "iopub.execute_input": "2026-06-07T23:32:34.312383Z",
"iopub.status.busy": "2026-06-07T23:27:30.503337Z", "iopub.status.busy": "2026-06-07T23:32:34.312250Z",
"iopub.status.idle": "2026-06-07T23:27:30.631863Z", "iopub.status.idle": "2026-06-07T23:32:34.430777Z",
"shell.execute_reply": "2026-06-07T23:27:30.631298Z" "shell.execute_reply": "2026-06-07T23:32:34.430189Z"
} }
}, },
"outputs": [ "outputs": [
@@ -497,30 +497,30 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "59991398", "id": "e5daac1f",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## 4. Fleet Assignment Model (FAM)\n", "## 4. Fleet Assignment Model (FAM) - Nível Brasil\n",
"Conforme o livro base *Airline Operations and Scheduling*, o problema de alocação de frotas busca atribuir os tipos de aeronaves às pernas de voo visando minimizar o custo e as perdas de receita (spill). Como temos apenas um tipo (C-97), nosso problema adapta-se a uma **alocação de esquadrões (e suas respectivas bases)** aos voos requeridos para minimizar o custo total de operação, que é proporcional à distância voada.\n", "Conforme o MODELO 1 solicitado (aeronaves podem pernoitar em qualquer hub), não há restrição de base ou esquadrão específico. Toda a frota de C-97 opera a nível nacional.\n",
"\n", "\n",
"**Modelo Matemático:**\n", "**Modelo Matemático:**\n",
"- **Variáveis de decisão:** $X_{s, r}$ (Quantidade de voos operados pelo esquadrão $s$ no trecho $r$)\n", "- **Variáveis de decisão:** $X_r$ (Quantidade de voos alocados no trecho $r$)\n",
"- **Função Objetivo:** Minimizar a distância total (Distância de posicionamento da base + Distância do trecho + Retorno para a base).\n", "- **Função Objetivo:** Minimizar a distância total da operação (Distância do trecho $\\times$ voos no trecho). Como as aeronaves não precisam retornar a uma base fixa, desconsideramos voos de posicionamento.\n",
"- **Restrições:**\n", "- **Restrições:**\n",
" 1. O número de voos alocados para o trecho deve suprir a demanda média (Média PAX / Capacidade do C-97).\n", " 1. O número de voos alocados para o trecho deve suprir a demanda diária operada daquela rota.\n",
" 2. O total de voos atribuídos a um esquadrão não pode exceder o número de aeronaves disponíveis naquele esquadrão no dia.\n" " 2. O total de voos atribuídos no dia não pode exceder o total da frota de C-97 disponível (14 aeronaves).\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 6,
"id": "be919c7c", "id": "26694904",
"metadata": { "metadata": {
"execution": { "execution": {
"iopub.execute_input": "2026-06-07T23:27:30.633209Z", "iopub.execute_input": "2026-06-07T23:32:34.432272Z",
"iopub.status.busy": "2026-06-07T23:27:30.633086Z", "iopub.status.busy": "2026-06-07T23:32:34.432145Z",
"iopub.status.idle": "2026-06-07T23:27:30.740716Z", "iopub.status.idle": "2026-06-07T23:32:34.456734Z",
"shell.execute_reply": "2026-06-07T23:27:30.739554Z" "shell.execute_reply": "2026-06-07T23:32:34.456266Z"
} }
}, },
"outputs": [ "outputs": [
@@ -528,41 +528,23 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"== RESULTADO DO FLEET ASSIGNMENT ==\n", "== RESULTADO DO FLEET ASSIGNMENT (NÍVEL BRASIL) ==\n",
"Status: Optimal\n", "Status: Optimal\n",
"Distância Total Minimizada: 15903.36 km\n", "Distância Total da Operação: 5703.02 km\n",
"\n", "\n",
"Alocações (Voos Diários):\n", "Alocações (Voos Diários Globais):\n",
"Trecho: SBGL-SBBR | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBBR-SBGL | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBBR-SBGR | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBGR-SBBR | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBGL-SBGR | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBSJ-SBBR | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBGR-SBGL | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBSJ-SBGL | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBGL-SBSJ | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"Trecho: SBBR-SBAN | Demanda Média PAX: 30 | Alocados: 1 voo(s)\n",
"\n", "\n",
"Trecho: SBGL-SBBR (Voos necessários: 1)\n", "Total de voos alocados na malha diária: 10 de 14 aeronaves disponíveis.\n"
" -> ETA6 (Base SBBR): 1 voo(s)\n",
"\n",
"Trecho: SBBR-SBGL (Voos necessários: 1)\n",
" -> ETA2 (Base SBNT): 1 voo(s)\n",
"\n",
"Trecho: SBBR-SBGR (Voos necessários: 1)\n",
" -> ETA5 (Base SBCO): 1 voo(s)\n",
"\n",
"Trecho: SBGR-SBBR (Voos necessários: 1)\n",
" -> ETA6 (Base SBBR): 1 voo(s)\n",
"\n",
"Trecho: SBGL-SBGR (Voos necessários: 1)\n",
" -> ETA3 (Base SBGL): 1 voo(s)\n",
"\n",
"Trecho: SBSJ-SBBR (Voos necessários: 1)\n",
" -> ETA6 (Base SBBR): 1 voo(s)\n",
"\n",
"Trecho: SBGR-SBGL (Voos necessários: 1)\n",
" -> PAMA SP (Base SBGR): 1 voo(s)\n",
"\n",
"Trecho: SBSJ-SBGL (Voos necessários: 1)\n",
" -> ETA3 (Base SBGL): 1 voo(s)\n",
"\n",
"Trecho: SBGL-SBSJ (Voos necessários: 1)\n",
" -> ETA3 (Base SBGL): 1 voo(s)\n",
"\n",
"Trecho: SBBR-SBAN (Voos necessários: 1)\n",
" -> ETA6 (Base SBBR): 1 voo(s)\n"
] ]
} }
], ],
@@ -570,7 +552,7 @@
"# Preparação dos dados para o modelo\n", "# Preparação dos dados para o modelo\n",
"rotas_lista = top_trechos['Trecho'].tolist()\n", "rotas_lista = top_trechos['Trecho'].tolist()\n",
"\n", "\n",
"# Dicionário de distâncias\n", "# Dicionário de distâncias apenas dos trechos diretos\n",
"def calc_distancia(icao1, icao2):\n", "def calc_distancia(icao1, icao2):\n",
" try:\n", " try:\n",
" lat1, lon1, alt1 = df_aeroportos.loc[icao1, ['lat', 'lon', 'elevation']]\n", " lat1, lon1, alt1 = df_aeroportos.loc[icao1, ['lat', 'lon', 'elevation']]\n",
@@ -583,52 +565,46 @@
"distancias_voo = {}\n", "distancias_voo = {}\n",
"for r in rotas_lista:\n", "for r in rotas_lista:\n",
" origem, destino = r.split('-')\n", " origem, destino = r.split('-')\n",
" dist_trecho = calc_distancia(origem, destino)\n", " distancias_voo[r] = calc_distancia(origem, destino)\n",
" for esq, base in bases_esquadroes.items():\n",
" dist_ida = calc_distancia(base, origem) if base != origem else 0\n",
" dist_volta = calc_distancia(destino, base) if base != destino else 0\n",
" distancias_voo[(esq, r)] = dist_ida + dist_trecho + dist_volta\n",
"\n", "\n",
"# Demanda de voos por rota (Média diária)\n", "# Demanda de voos por rota\n",
"voos_requeridos = {}\n", "voos_requeridos = {}\n",
"for _, row in top_trechos.iterrows():\n", "for _, row in top_trechos.iterrows():\n",
" # Arredondamento para cima da (demanda / capacidade)\n",
" voos = math.ceil(row['Media_PAX_Dias_Operados'] / CAPACIDADE_C97)\n", " voos = math.ceil(row['Media_PAX_Dias_Operados'] / CAPACIDADE_C97)\n",
" # Pelo menos 1 voo para suprir a demanda da rota selecionada\n",
" voos_requeridos[row['Trecho']] = max(1, voos)\n", " voos_requeridos[row['Trecho']] = max(1, voos)\n",
"\n", "\n",
"# MODELAGEM COM PULP\n", "# MODELAGEM COM PULP\n",
"prob = pulp.LpProblem(\"Fleet_Assignment_C97\", pulp.LpMinimize)\n", "prob = pulp.LpProblem(\"Fleet_Assignment_C97_Brasil\", pulp.LpMinimize)\n",
"\n", "\n",
"# Variáveis\n", "# Variáveis (número de voos em cada rota)\n",
"X = pulp.LpVariable.dicts(\"X\", [(esq, r) for esq in aeronaves_por_esquadrao.keys() for r in rotas_lista], lowBound=0, cat='Integer')\n", "X = pulp.LpVariable.dicts(\"X\", rotas_lista, lowBound=0, cat='Integer')\n",
"\n", "\n",
"# Função Objetivo\n", "# Função Objetivo: Minimizar distância voada nos trechos\n",
"prob += pulp.lpSum([distancias_voo[(esq, r)] * X[(esq, r)] for esq in aeronaves_por_esquadrao.keys() for r in rotas_lista])\n", "prob += pulp.lpSum([distancias_voo[r] * X[r] for r in rotas_lista])\n",
"\n", "\n",
"# Restrição 1: Suprir a demanda da rota\n", "# Restrição 1: Suprir a demanda\n",
"for r in rotas_lista:\n", "for r in rotas_lista:\n",
" prob += pulp.lpSum([X[(esq, r)] for esq in aeronaves_por_esquadrao.keys()]) >= voos_requeridos[r]\n", " prob += X[r] >= voos_requeridos[r]\n",
"\n", "\n",
"# Restrição 2: Limite de aeronaves por esquadrão\n", "# Restrição 2: Limite global de frota (nível Brasil)\n",
"for esq in aeronaves_por_esquadrao.keys():\n", "prob += pulp.lpSum([X[r] for r in rotas_lista]) <= total_aeronaves\n",
" prob += pulp.lpSum([X[(esq, r)] for r in rotas_lista]) <= aeronaves_por_esquadrao[esq]\n",
"\n", "\n",
"# Solução\n", "# Solução\n",
"prob.solve(pulp.PULP_CBC_CMD(msg=False))\n", "prob.solve(pulp.PULP_CBC_CMD(msg=False))\n",
"\n", "\n",
"# Exibição dos resultados\n", "# Exibição dos resultados\n",
"print(\"== RESULTADO DO FLEET ASSIGNMENT ==\")\n", "print(\"== RESULTADO DO FLEET ASSIGNMENT (NÍVEL BRASIL) ==\")\n",
"print(f\"Status: {pulp.LpStatus[prob.status]}\")\n", "print(f\"Status: {pulp.LpStatus[prob.status]}\")\n",
"print(f\"Distância Total Minimizada: {pulp.value(prob.objective):.2f} km\\n\")\n", "print(f\"Distância Total da Operação: {pulp.value(prob.objective):.2f} km\\n\")\n",
"\n", "\n",
"print(\"Alocações (Voos Diários):\")\n", "print(\"Alocações (Voos Diários Globais):\")\n",
"voos_totais = 0\n",
"for r in rotas_lista:\n", "for r in rotas_lista:\n",
" print(f\"\\nTrecho: {r} (Voos necessários: {voos_requeridos[r]})\")\n", " qtd = int(X[r].varValue)\n",
" for esq in aeronaves_por_esquadrao.keys():\n", " voos_totais += qtd\n",
" qtd = int(X[(esq, r)].varValue)\n", " print(f\"Trecho: {r:10} | Demanda Média PAX: {voos_requeridos[r]*CAPACIDADE_C97:3.0f} | Alocados: {qtd} voo(s)\")\n",
" if qtd > 0:\n", "\n",
" print(f\" -> {esq} (Base {bases_esquadroes[esq]}): {qtd} voo(s)\")\n" "print(f\"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.\")\n"
] ]
} }
], ],