Allocazione Budget con Python e MILP: Modelli, Codice e Soluzioni Pratiche

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
MILP per l'allocazione del budget

 

Perché usare la MILP per allocare un budget?

Allocare un budget può sembrare, a prima vista, un problema molto semplice: abbiamo una quantità limitata di denaro e dobbiamo decidere come distribuirla tra diversi progetti.

Nella realtà, però, le decisioni di investimento raramente si riducono a un semplice confronto tra costo e rendimento. Un progetto può essere indivisibile, due iniziative possono essere incompatibili, una determinata area può richiedere una spesa minima oppure la scelta di un progetto può rendere obbligatoria la selezione di un altro.

È proprio in situazioni di questo tipo che la Programmazione Lineare Intera Mista (MILP, Mixed-Integer Linear Programming) diventa uno strumento particolarmente utile.

L’idea fondamentale è trasformare le decisioni aziendali in variabili matematiche, descrivere gli obiettivi attraverso una funzione da massimizzare o minimizzare e rappresentare le regole operative mediante vincoli.

1. Le decisioni non sono sempre continue

Nei problemi di ottimizzazione lineare tradizionali, una variabile può assumere qualsiasi valore reale compatibile con i vincoli. Ma molte decisioni aziendali non hanno questa caratteristica.

Supponiamo, per esempio, di dover decidere se finanziare o meno un progetto strategico.

Non avrebbe necessariamente senso finanziare il 37% del progetto: il progetto viene realizzato oppure non viene realizzato.

Possiamo quindi introdurre una variabile binaria:

[math]\displaystyle
x_i =
\begin{cases}
1 & \text{se il progetto } i \text{ viene selezionato} \\
0 & \text{altrimenti}
\end{cases}
[/math]

La variabile può assumere soltanto i valori:

[math]x_i \in \{0, 1\}[/math]

Questo semplice passaggio permette di rappresentare matematicamente decisioni del tipo:

  • finanziare/non finanziare;
  • aprire/non aprire una sede;
  • acquistare/non acquistare un macchinario;
  • assumere/non assumere una risorsa;
  • attivare/non attivare un servizio.

La programmazione intera permette quindi di incorporare nel modello una caratteristica fondamentale delle decisioni reali: alcune scelte sono discrete e non possono essere trattate come quantità frazionabili.

Nota: i modelli con sole variabili binarie, come quelli dei primi esercizi, sono un caso particolare della programmazione intera. La MILP estende questa logica permettendo di combinare variabili intere o binarie con variabili continue.

2. Le decisioni sono interdipendenti

Un secondo elemento fondamentale è che le decisioni raramente sono indipendenti.

Supponiamo di avere quattro progetti A, B, C e D e un budget limitato.

La scelta di finanziare A modifica immediatamente il budget disponibile per gli altri progetti:

[math]B_{\text{residuo}} = B – c_A[/math]

Se invece vengono selezionati A e C:

[math]B_{\text{residuo}} = B – c_A – c_C[/math]

Di conseguenza, il valore di un progetto non può essere valutato sempre in isolamento.

Un progetto con un rapporto VAN/costo elevato potrebbe essere escluso perché occupa una quota di budget che impedisce di combinare altri progetti producendo un valore complessivo maggiore.

È questa una delle differenze fondamentali tra una decisione locale e una decisione di portafoglio:

non dobbiamo necessariamente scegliere i progetti migliori singolarmente; dobbiamo trovare la combinazione migliore nel rispetto di tutti i vincoli.

Nel modello matematico questa interdipendenza viene rappresentata, ad esempio, dal vincolo di budget:

[math]\displaystyle \sum_{i \in I} c_i x_i \leq B[/math]

La decisione relativa a ciascun progetto è quindi collegata a tutte le altre attraverso una risorsa condivisa: il budget.

3. I vincoli aziendali sono spesso logici

Nella pratica, inoltre, le organizzazioni devono rispettare regole che non possono essere rappresentate soltanto attraverso costi e ricavi.

Alcuni esempi:

  • almeno una iniziativa per area;
  • massimo tre progetti finanziabili;
  • se viene scelto A, deve essere scelto anche B;
  • A e C non possono essere selezionati contemporaneamente;
  • almeno il 20% del budget deve essere destinato all’innovazione.

Queste condizioni possono essere tradotte in vincoli matematici.

Per esempio, se al massimo possiamo selezionare tre progetti:

[math]\displaystyle \sum_i x_i \leq 3[/math]

Se la scelta di A implica necessariamente la scelta di B:

[math]x_A \leq x_B[/math]

Se A e C sono incompatibili:

[math]x_A + x_C \leq 1[/math]

Se invece almeno il 20% del budget deve essere destinato a un insieme di progetti appartenenti all’area innovazione:

[math]\displaystyle \sum_{i \in I_{\text{innovazione}}} c_i x_i \geq 0{,}20 B[/math]

Questi vincoli trasformano regole decisionali espresse in linguaggio naturale in relazioni matematiche che un solver può elaborare.

Pubblicità

Dalla decisione aziendale al modello matematico

Possiamo quindi vedere la MILP come un ponte tra tre livelli:

Problema aziendale Modello matematico Solver
Come distribuiamo un budget limitato tra diverse iniziative? [math]\displaystyle \text{variabili decisionali} + \text{funzione obiettivo} + \text{vincoli}[/math] Qual è la combinazione di decisioni che massimizza il risultato rispettando tutte le condizioni?

Il vantaggio non è soltanto ottenere un numero finale. Un modello MILP permette di rendere esplicite, verificabili e riproducibili le regole che governano una decisione complessa.

Focus: Quanto cresce lo spazio delle soluzioni?

Per [math]n[/math] progetti binari (dove la scelta è strettamente “sì, lo realizzo” oppure “no, lo scarto”), esistono esattamente [math]2^n[/math] possibili combinazioni.

Lo spazio delle soluzioni cresce in modo esponenziale:

Numero di Progetti ([math]n[/math]) Combinazioni Possibili ([math]2^n[/math])
4 16
10 1.024
20 1.048.576
30 1.073.741.824
50 [math]1{,}13 \times 10^{15}[/math]

Dal calcolo manuale all’ottimizzazione

Questa progressione mostra chiaramente il salto di complessità. Finché il perimetro è ridotto, posso verificare manualmente l’Esercizio 1 mappando e calcolando tutte le 16 alternative. Tuttavia, la forza bruta diventa rapidamente insostenibile: già con 50 progetti parliamo di oltre un milione di miliardi di scenari. Ecco perché per problemi reali serve un solver: un algoritmo di ottimizzazione capace di tagliare i rami improduttivi e convergere alla soluzione migliore senza dover esplorare fisicamente l’intero universo delle possibilità.

Nei sei esercizi che seguono partiremo da un semplice problema di selezione binaria e introdurremo progressivamente vincoli più articolati. L’obiettivo è comprendere non soltanto come scrivere un modello MILP in Python con PuLP, ma soprattutto perché la formulazione matematica è utile per trasformare un problema decisionale reale in un problema di ottimizzazione risolvibile automaticamente.

Forse potrebbe interessarti anche:  Quadratura dei Dati e Mass Balance: Guida ai Controlli di Integrità in SQL e Python

Esercizi

Esercizio 1: Allocazione Base tra Progetti Indipendenti

Testo:
Un’azienda di consulenza, “DataMind S.r.l.”, ha un budget annuale di € 120.000 da destinare a quattro potenziali progetti di sviluppo software (A, B, C, D). Ogni progetto prevede un costo totale e un ritorno economico stimato (valore attuale netto, VAN), come indicato in tabella. L’obiettivo è massimizzare il VAN totale, potendo finanziare interamente o meno ciascun progetto.

Progetto Costo (€) VAN (€)
A 40.000 90.000
B 60.000 120.000
C 50.000 100.000
D 30.000 70.000

Formulare un modello MILP, risolverlo e fornire la composizione del portafoglio progetti ottimale.

Risoluzione:

Formulazione Matematica:

  • Insiemi: [math]I = \{A, B, C, D\}[/math]
  • Dati: Costo [math]c_i[/math], VAN [math]v_i[/math], Budget [math]B = 120.000[/math].
  • Variabili Decisionali (Binarie):
    [math]x_i = 1[/math] se il progetto [math]i[/math] è selezionato, 0 altrimenti.
  • Funzione Obiettivo (Massimizzazione):
    [math]\displaystyle \text{Max } Z = \sum_{i \in I} v_i \cdot x_i[/math]
  • Vincolo di Budget:
    [math]\displaystyle \sum_{i \in I} c_i \cdot x_i \le B[/math]
  • Dominio: [math]x_i \in \{0, 1\}[/math]

Implementazione in Python con PuLP:

import pulp

# 1. Definizione del problema
model = pulp.LpProblem("Max_VAN_Budget", pulp.LpMaximize)

# 2. Dati
progetti = ['A', 'B', 'C', 'D']
costo = {'A': 40000, 'B': 60000, 'C': 50000, 'D': 30000}
van = {'A': 90000, 'B': 120000, 'C': 100000, 'D': 70000}
budget_totale = 120000

# 3. Variabili decisionali (dizionario di variabili binarie)
x = pulp.LpVariable.dicts("progetto", progetti, cat=pulp.LpBinary)

# 4. Funzione Obiettivo
model += pulp.lpSum([van[i] * x[i] for i in progetti]), "VAN_Total"

# 5. Vincolo di Budget
model += pulp.lpSum([costo[i] * x[i] for i in progetti]) <= budget_totale, "Budget_Constraint"

# 6. Risoluzione
solver = pulp.PULP_CBC_CMD(msg=True)  # 'msg=True' per mostrare i log del solver
model.solve(solver)

# 7. Output dei risultati
print(f"Stato: {pulp.LpStatus[model.status]}")
print(f"VAN Totale Massimizzato: € {pulp.value(model.objective):,.2f}")
print("\nComposizione Portafoglio:")
for i in progetti:
    if pulp.value(x[i]) == 1:
        print(f" - Progetto {i}: Selezionato (Costo: €{costo[i]:,}, VAN: €{van[i]:,})")
print(f"\nBudget Utilizzato: € {sum(costo[i] * pulp.value(x[i]) for i in progetti):,.2f}")
print(f"Budget Residuo: € {budget_totale - sum(costo[i] * pulp.value(x[i]) for i in progetti):,.2f}")
Stato: Optimal
VAN Totale Massimizzato: € 260,000.00

Composizione Portafoglio:
 - Progetto A: Selezionato (Costo: €40,000, VAN: €90,000)
 - Progetto C: Selezionato (Costo: €50,000, VAN: €100,000)
 - Progetto D: Selezionato (Costo: €30,000, VAN: €70,000)

Budget Utilizzato: € 120,000.00
Budget Residuo: € 0.00

 

Soluzione ottimale:

Eseguendo il codice, la soluzione ottimale è Progetti A, C e D.

  • Costo totale: 40.000 + 50.000 + 30.000 = € 120.000 (budget completamente saturato).
  • VAN totale: 90.000 + 100.000 + 70.000 = € 260.000.

Verifica: tutte le combinazioni fattibili:

Combinazione Costo Totale VAN Totale Fattibile?
A, C, D 120.000 260.000 ✅ Ottimale
A, B, D 130.000 280.000 ❌ Supera budget
A, B, C 150.000 310.000 ❌ Supera budget
B, C, D 140.000 290.000 ❌ Supera budget
A, C 90.000 190.000
B, D 90.000 190.000
A, D 70.000 160.000
A, B 100.000 210.000
B, C 110.000 220.000

Nessuna combinazione raggiunge o supera i 260.000 di VAN senza sforare il budget.

💡 Lezione chiave per la MILP
Questo esercizio dimostra perfettamente la potenza del solver MILP rispetto a un approccio euristico:

  • Il solver non si limita a confrontare il rapporto VAN/costo dei singoli progetti. Utilizza algoritmi di ottimizzazione intera, come Branch & Bound e tecniche di cutting, per identificare la soluzione ottima senza dover necessariamente enumerare esplicitamente tutte le combinazioni.
  • La soluzione ottimale sfrutta l’interezza del budget. Se il vincolo fosse stato [math]\le 120.000[/math], il solver ha trovato la combinazione che lo satura esattamente.
  • Questo è il classico esempio in cui un progetto “meno efficiente” (A ha rapporto 2.25, C ha 2.0, D ha 2.33) viene comunque scelto perché la loro combinazione è perfetta per lo spazio disponibile.

Il costo dell’intuito: perché l’approccio “Naïf” fallisce

Per comprendere appieno il valore di un solver MILP, mettiamolo a confronto con il metodo che la maggior parte delle aziende utilizza nella pratica quotidiana: l’approccio greedy (o ingordo), che consiste nell’ordinare i progetti dal più redditizio al meno redditizio e finanziarli in sequenza fino a esaurimento del budget.

Se ordiniamo i nostri quattro progetti per VAN assoluto decrescente:

Progetto Costo (€) VAN (€) Scelta Naïf (per VAN desc.) Budget Residuo
B 60.000 120.000 ✅ Selezionato 60.000 €
C 50.000 100.000 ✅ Selezionato 10.000 €
A 40.000 90.000 ❌ Scartato (fuori budget) 10.000 €
D 30.000 70.000 ❌ Scartato (fuori budget) 10.000 €

Il Bilancio del confronto:

  • Portafoglio Naïf (B + C): Costo totale € 110.000 | VAN totale = € 220.000 (con 10.000 € sprecati in cassa).
  • Portafoglio Ottimizzato MILP (A + C + D): Costo totale € 120.000 | VAN totale = € 260.000 (budget saturato perfettamente).

⚠️ La brutta sorpresa: Affidandosi all’istinto e scegliendo i due progetti con il VAN assoluto più alto, l’azienda lascia sul tavolo 40.000 € di valore. Il progetto B, apparentemente il migliore in assoluto, è così “ingombrante” da impedire la combinazione perfetta che avremmo ottenuto incastrando progetti più piccoli ma sinergici tra loro. La MILP serve esattamente a evitare questo genere di trappole cognitive.


Esercizio 2: Allocazione con Soglia Minima di Spesa per Categoria

Testo:
La “GreenEnergy Corp” deve allocare un budget di € 500.000 per iniziative di sostenibilità in tre aree: Rinnovabili (R), Efficienza Energetica (E), e Biodiversità (B). Per ogni area, sono disponibili più progetti, ognuno con un costo e un impatto ambientale (espresso in “punti impatto”). Per garantire un piano equilibrato, l’azienda impone che la spesa in ciascuna area sia almeno il 15% del budget totale. L’obiettivo è massimizzare l’impatto ambientale totale.

Area Progetto Costo (€) Impatto
Rinnovabili (R) R1 120.000 250
R2 90.000 180
Efficienza (E) E1 80.000 200
E2 150.000 310
Biodiversità (B) B1 60.000 120
B2 110.000 230
Forse potrebbe interessarti anche:  Guida Completa a Polars per Python: L'Alternativa Veloce a Pandas (Esempi e Tutorial)

Formulare e risolvere il problema.

Risoluzione:

Formulazione Matematica:

  • Insiemi: [math]I = \{R1, R2, E1, E2, B1, B2\}[/math]
  • Dati: Costo [math]c_i[/math], Impatto [math]p_i[/math], Area [math]a_i[/math], Budget [math]B = 500.000[/math], Soglia Minima [math]s = 0.15[/math].
  • Variabili: [math]x_i \in \{0, 1\}[/math]
  • Funzione Obiettivo: [math]\displaystyle \text{Max } Z = \sum p_i \cdot x_i[/math]
  • Vincoli:
    • Budget: [math]\displaystyle \sum c_i \cdot x_i \le 500.000[/math]
    • Soglia per R: [math]\displaystyle \sum_{i \in R} c_i \cdot x_i \ge 75.000[/math]
    • Soglia per E: [math]\displaystyle \sum_{i \in E} c_i \cdot x_i \ge 75.000[/math]
    • Soglia per B: [math]\displaystyle \sum_{i \in B} c_i \cdot x_i \ge 75.000[/math]
  • Dominio: [math]x_i \in \{0, 1\}[/math]

Implementazione in Python con PuLP:

import pulp

# Definizione del problema
model = pulp.LpProblem("Budget_Sostenibilita", pulp.LpMaximize)

# Dati
progetti = ['R1', 'R2', 'E1', 'E2', 'B1', 'B2']
area = {'R1': 'R', 'R2': 'R', 'E1': 'E', 'E2': 'E', 'B1': 'B', 'B2': 'B'}
costo = {'R1': 120000, 'R2': 90000, 'E1': 80000, 'E2': 150000, 'B1': 60000, 'B2': 110000}
impatto = {'R1': 250, 'R2': 180, 'E1': 200, 'E2': 310, 'B1': 120, 'B2': 230}
budget_totale = 500000
soglia_min = 0.15 * budget_totale

# Variabili
x = pulp.LpVariable.dicts("progetto", progetti, cat=pulp.LpBinary)

# Funzione Obiettivo
model += pulp.lpSum([impatto[i] * x[i] for i in progetti])

# Vincolo Budget
model += pulp.lpSum([costo[i] * x[i] for i in progetti]) <= budget_totale

# Vincoli di Soglia Minima per Area
for a in set(area.values()):
    model += pulp.lpSum([costo[i] * x[i] for i in progetti if area[i] == a]) >= soglia_min, f"Soglia_{a}"

# Risoluzione
model.solve(pulp.PULP_CBC_CMD(msg=False))

# Output
print(f"Stato: {pulp.LpStatus[model.status]}")
print(f"Impatto Totale Massimizzato: {pulp.value(model.objective):.0f} punti")
print("\nComposizione Portafoglio:")
spesa_tot = 0
for a in set(area.values()):
    spesa_area = sum(costo[i] * pulp.value(x[i]) for i in progetti if area[i] == a)
    print(f"Area {a}: Spesa = €{spesa_area:,.2f} (Soglia: €{soglia_min:,.2f})")
    for i in [p for p in progetti if area[p] == a]:
        if pulp.value(x[i]) == 1:
            print(f"  - {i}: Selezionato")
print(f"Budget Totale Utilizzato: €{sum(costo[i] * pulp.value(x[i]) for i in progetti):,.2f}")
Stato: Optimal
Impatto Totale Massimizzato: 1040 punti

Composizione Portafoglio:
Area R: Spesa = €90,000.00 (Soglia: €75,000.00)
  - R2: Selezionato
Area B: Spesa = €170,000.00 (Soglia: €75,000.00)
  - B1: Selezionato
  - B2: Selezionato
Area E: Spesa = €230,000.00 (Soglia: €75,000.00)
  - E1: Selezionato
  - E2: Selezionato
Budget Totale Utilizzato: €490,000.00

 

Verifica dei vincoli

Area Progetti selezionati Spesa Soglia minima Rispettata?
R (Rinnovabili) R2 € 90.000 € 75.000 ✅ Sì
E (Efficienza) E1, E2 € 80.000 + € 150.000 = € 230.000 € 75.000 ✅ Sì
B (Biodiversità) B1, B2 € 60.000 + € 110.000 = € 170.000 € 75.000 ✅ Sì
Totale € 490.000 € 500.000 (budget) ✅ Sì

Perché questa è la soluzione ottimale?

Il solver ha scelto R2 invece di R1 (che ha impatto 250, superiore a 180) per un motivo strategico:

  • Se avesse scelto R1 (costo 120.000), il budget residuo per E e B sarebbe stato 500.000 – 120.000 = 380.000.
  • Dovendo rispettare le soglie minime, per l’area B sarebbe stato necessario prendere B2 (110.000) o B1+B2 (170.000).
  • Per massimizzare l’impatto, il solver ha valutato tutte le combinazioni e ha scoperto che rinunciare a R1 (perdendo 70 punti di impatto) permette di includere sia B1 che B2 (guadagnando 120 + 230 = 350 punti) e di mantenere E1+E2 (510 punti). Il bilancio netto è favorevole.

Il risultato è un portafoglio bilanciato (tutte le aree ricevono almeno il 15% del budget) e ad alto impatto (1040 punti), che è il massimo raggiungibile con questi vincoli.

Pubblicità

💡 Osservazione:
I vincoli di soglia minima sono “vincoli di lower bound” su somme di variabili. Forzano il modello a considerare progetti che altrimenti potrebbero essere scartati perché meno efficienti, garantendo un portafoglio diversificato e allineato con le priorità strategiche.

Esercizio 3: Allocazione multi-periodo con vincoli di cassa trimestrali

Testo:
“HealthTech Innovations” pianifica il lancio di tre nuovi dispositivi medici (H1, H2, H3). Il budget totale annuale è di € 200.000, suddiviso in quattro trimestri (Q1, Q2, Q3, Q4) con vincoli di cassa separati: [€ 50.000, € 60.000, € 50.000, € 40.000]. Le spese e i ricavi previsti per ogni dispositivo sono distribuiti sui trimestri. Un dispositivo può essere avviato solo se viene finanziato completamente; l’obiettivo è massimizzare il profitto netto totale.

Dispositivo Costo Q1 Costo Q2 Costo Q3 Costo Q4 Ricavo
H1 30.000 20.000 10.000 0 100.000
H2 10.000 30.000 30.000 20.000 120.000
H3 40.000 10.000 20.000 10.000 110.000

Formulare e risolvere.

Risoluzione:

Formulazione Matematica:

  • Insiemi: [math]I = \{H1, H2, H3\}[/math], [math]T = \{Q1, Q2, Q3, Q4\}[/math].
  • Dati: Costo [math]c_{i,t}[/math], Ricavo [math]r_i[/math], Budget [math]B_t[/math].
  • Variabili: [math]x_i \in \{0, 1\}[/math]
  • Funzione Obiettivo:
    [math]\displaystyle \text{Max } Z = \sum_{i} \left( r_i – \sum_{t} c_{i,t} \right) \cdot x_i = \sum_{i} \text{Profitto}_i \cdot x_i[/math]
  • Vincoli di Budget Trimestrali: Per ogni trimestre [math]t[/math], [math]\displaystyle \sum_{i} c_{i,t} \cdot x_i \le B_t[/math].
  • Dominio: [math]x_i \in \{0, 1\}[/math]

Implementazione in Python con PuLP:

import pulp

model = pulp.LpProblem("Budget_Dinamico", pulp.LpMaximize)

dispositivi = ['H1', 'H2', 'H3']
trimestri = ['Q1', 'Q2', 'Q3', 'Q4']
budget = {'Q1': 50000, 'Q2': 60000, 'Q3': 50000, 'Q4': 40000}

# Costi: [dispositivo][trimestre]
costi = {
    'H1': {'Q1': 30000, 'Q2': 20000, 'Q3': 10000, 'Q4': 0},
    'H2': {'Q1': 10000, 'Q2': 30000, 'Q3': 30000, 'Q4': 20000},
    'H3': {'Q1': 40000, 'Q2': 10000, 'Q3': 20000, 'Q4': 10000}
}
ricavi = {'H1': 100000, 'H2': 120000, 'H3': 110000}

# Calcolo profitto netto
profitto = {i: ricavi[i] - sum(costi[i].values()) for i in dispositivi}

# Variabili
x = pulp.LpVariable.dicts("dispositivo", dispositivi, cat=pulp.LpBinary)

# Funzione Obiettivo
model += pulp.lpSum([profitto[i] * x[i] for i in dispositivi])

# Vincoli di Budget per trimestre
for t in trimestri:
    model += pulp.lpSum([costi[i][t] * x[i] for i in dispositivi]) <= budget[t], f"Budget_{t}"

# Risoluzione
model.solve(pulp.PULP_CBC_CMD(msg=False))

# Output
print(f"Stato: {pulp.LpStatus[model.status]}")
profitto_tot = pulp.value(model.objective)
print(f"Profitto Netto Totale: € {profitto_tot:,.2f}")
print("\nDispositivi Selezionati:")
for i in dispositivi:
    if pulp.value(x[i]) == 1:
        print(f" - {i}")
print("\nUtilizzo Budget per Trimestre:")
for t in trimestri:
    spesa = sum(costi[i][t] * pulp.value(x[i]) for i in dispositivi)
    print(f"  {t}: € {spesa:,.2f} / € {budget[t]:,.2f}")
Stato: Optimal
Profitto Netto Totale: € 70,000.00

Dispositivi Selezionati:
 - H1
 - H2

Utilizzo Budget per Trimestre:
  Q1: € 40,000.00 / € 50,000.00
  Q2: € 50,000.00 / € 60,000.00
  Q3: € 40,000.00 / € 50,000.00
  Q4: € 20,000.00 / € 40,000.00

Forse potrebbe interessarti anche:  Da Statistico a Data Scientist: 20 Anni Dentro l'Evoluzione di una Professione

 Analisi dettagliata della soluzione ottimale

Vediamo nel dettaglio perché la soluzione H1 + H2 è ottimale e quali insegnamenti possiamo trarre.

✅ Verifica della fattibilità

Trimestre Budget Spesa H1 Spesa H2 Spesa Totale Rispettato?
Q1 50.000 30.000 10.000 40.000 ✅ Sì (avanzo 10k)
Q2 60.000 20.000 30.000 50.000 ✅ Sì (avanzo 10k)
Q3 50.000 10.000 30.000 40.000 ✅ Sì (avanzo 10k)
Q4 40.000 0 20.000 20.000 ✅ Sì (avanzo 20k)

Profitto netto:

  • H1: Ricavo 100.000 − Costo 60.000 = 40.000 €
  • H2: Ricavo 120.000 − Costo 90.000 = 30.000 €
  • Totale = 70.000 €

🔍 Perché H3 non è stato scelto?

H3 ha un profitto netto di:
[math]110.000[/math] (ricavo) − [math]80.000[/math] (costo totale) = 30.000 €

A prima vista, H3 è altrettanto redditizio di H2 (entrambi 30.000). Tuttavia, la distribuzione temporale dei costi rende H3 meno compatibile:

Combinazione Costo Q1 Costo Q2 Costo Q3 Costo Q4 Profitto Fattibile?
H1 + H2 40.000 50.000 40.000 20.000 70.000 ✅ Sì
H1 + H3 70.000 30.000 30.000 10.000 70.000 ❌ No (Q1 supera 50k)
H2 + H3 50.000 40.000 50.000 30.000 60.000 ✅ Sì (ma profitto inferiore)

Il problema è che H1 e H3 insieme richiederebbero 70.000 € nel primo trimestre, superando il budget di Q1 (50.000 €). Quindi, per poter includere H3, dovremmo rinunciare a H1, ma allora il profitto scenderebbe a 60.000 € (H2+H3), inferiore ai 70.000 € di H1+H2.

💡 Osservazione strategica

Questo esercizio illustra perfettamente il concetto di vincoli di cassa temporali (o cash flow constraints):

  • Il profitto annuale non è l’unico criterio: la distribuzione temporale dei costi è cruciale. Un progetto con lo stesso profitto può essere scartato se concentra le spese in un periodo con budget limitato.
  • I budget trimestrali creano “colli di bottiglia” che possono impedire combinazioni altrimenti profittevoli. In questo caso, il primo trimestre è il vincolo più stringente (solo 50.000 €), e ha determinato l’esclusione di H3.
  • Con tre variabili binarie esistono [math]2^3 = 8[/math] combinazioni teoriche. In questo caso, essendo il problema molto piccolo, possiamo verificarle manualmente per comprendere perché H1 + H2 sia la soluzione ottimale. Un solver MILP, invece, utilizza algoritmi come Branch & Bound, presolve e cutting planes per affrontare problemi molto più grandi senza dover necessariamente enumerare esplicitamente tutte le combinazioni.

Confronto delle alternative

Combinazione Profitto Fattibile? Motivo esclusione
H1 + H2 70.000 ✅ Sì Soluzione ottimale
H1 + H3 70.000 ❌ No Q1: 70.000 > 50.000
H2 + H3 60.000 ✅ Sì Profitto inferiore
H1 + H2 + H3 100.000 ❌ No Q2: 80.000 > 60.000, Q3: 70.000 > 50.000
Solo H1 40.000 ✅ Sì Profitto inferiore
Solo H2 30.000 ✅ Sì Profitto inferiore
Solo H3 30.000 ✅ Sì Profitto inferiore

🎯 Domanda di riflessione per te
“Perché il modello non ha scelto H1 + H3, che ha lo stesso profitto di H1 + H2 (70.000 €)?”

Risposta: Perché H1 + H3 viola il vincolo di budget del primo trimestre (70.000 € richiesti contro 50.000 € disponibili). Questo dimostra che i vincoli di capacità temporale possono rendere inammissibile una combinazione che sarebbe redditizia in un modello con un solo vincolo di budget annuale.

Analisi degli Esercizi e Spunti Applicativi

Esercizio 1 (Allocazione Base): Il fallimento del ROI

Questo esercizio è la variante aziendale del classico Knapsack Problem (Problema dello Zaino). La sua peculiarità sta nel dimostrare che ordinare i progetti per efficienza (il rapporto VAN/Costo) porta spesso a soluzioni sub-ottimali. Il progetto B ha un VAN altissimo, ma è troppo “ingombrante”. La MILP insegna che l’ottimo globale non è quasi mai la semplice somma degli ottimi locali.

Esercizio 2 (Soglie Minime): La traduzione della strategia in matematica

Questo è il caso d’uso più frequente nelle grandi organizzazioni (es. criteri ESG, quote di innovazione, bilanciamento geografico). L’esercizio è affascinante perché il solver fa una scelta “controintuitiva”: scarta il progetto R1 (molto efficiente) a favore di R2, pur di liberare il budget esatto necessario per far scattare i vincoli minimi delle altre categorie. Mostra come la MILP impedisca alle “regole aziendali” di distruggere più valore del necessario.

Esercizio 3 (Vincoli Multi-periodo): Il tempo come risorsa

È l’esercizio più maturo. Spesso i budget vengono approvati su base annuale, ma i flussi di cassa escono mensilmente. Dimostra come un progetto teoricamente validissimo (H3) diventi inammissibile a causa di un “collo di bottiglia” temporale (il budget del Q1). Questo è esattamente il motivo per cui i CFO bloccano progetti apparentemente perfetti: il modello MILP integra questa saggezza finanziaria direttamente nell’algoritmo.

📚 Per approfondire: Ottimizzazione, programmazione lineare e Python

Se vuoi approfondire le tecniche di ottimizzazione matematica e programmazione lineare, dalla gestione della produzione e delle scorte ai problemi di assegnazione e trasporto, questi articoli mostrano metodi, algoritmi e implementazioni pratiche in Python:

👉 Ottimizzazione della produzione e gestione scorte: modello di programmazione lineare intera e implementazione Python

👉 Metodo Branch and Bound spiegato: esempio semplice di PLI

👉 Risolvere problemi di ottimizzazione lineare: confronto pratico tra PuLP e SciPy Optimize

👉 Librerie Python per l’ottimizzazione: un viaggio completo

👉 Problema di assegnazione: come risolverlo con Python e PuLP per minimizzare i costi

👉 Ottimizza i trasporti con PuLP

 

Pubblicità