Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da...

11

Click here to load reader

description

Programmazione Lineare – Schedulazioni (Diagrammi di Gantt e di Pert), labirinti, problemi dello zaino (knapsack). La potenza della programmazione lineare e il pacchetto software Gnu (sia per Windows che Unix/Linux)

Transcript of Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da...

Page 1: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

Programmazione Lineare – Schedulazioni vincenti, labirinti

tenebrosi, con il bastone da maresciallo nello zaino ing. R. Turco ([email protected])

Il vantaggio di usare strumenti LP è di astrarsi dalla programmazione software e

concentrarsi solo sulla formulazione matematica. Ciò non toglie che si può

programmare in progetti più grandi con C/C++/Java e librerie LP disponibili, come le

API offerte da GNUWin32 o anche per Linux/Unix con l'uso di Database.

Vediamo un classico: dovete fare con Microsoft Project un diagramma di GANTT con

risorse, task in cascata o parallelizzabile e le milestone con ogni opportuna data e

descrizione; poi vi hanno richiesto il diagramma di Pert per comprendere i task critici.

Sapete l'inizio e la fine desiderata, quanto serve per ogni attività, quante persone, ma

occorre far entrare il tutto nel periodo richiesto. E' possibile o impossibile? Come e

quali attività si devono parallelizzare? Quello ottenuto è il meglio possibile o esistono

anche altre soluzioni?

Al di là di ulteriori elementi che dipendono dal tipo di lavoro in gioco, stiamo parlando

di un problema di pianificazione o scheduling, che rientra tra i problemi NP-completi!

In sostanza le stesse informazioni accennate sopra, come barre nel GANTT e le

timeline si possono tradurre in un grafo matematico dove i nodi possono essere i nomi

dei task, gli archi orientati il cambio di sequenza da un task ad un altro (e la

dipendenza anche tra essi sequenziale), mentre i pesi sugli archi possono

rappresentare il tempo necessario allo svolgimento del task. Nel grafo potremmo

avere anche parallelizzazioni di task , cioè situazioni in cui da un nodo escono

contemporaneamente più archi verso altri nodi.

Con un grafo, quindi, si possono formulare varie tipologie di problemi. Per grafo pesato

orientato intendiamo una struttura G(N, A), con N insieme dei nodi e cardinalità | N |,

ed A insieme degli archi e cardinalità | A |.

Page 2: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

L'insieme degli archi, A, è caratterizzato da un orientamento da nodo origine ad uno

destinazione i,j.

In generale l'obiettivo di un "problema di scheduling" matematico è di ottimizzare una

o più grandezze di performance tra le seguenti:

minimizzare la massima "lateness" o il massimo ritardo (non dite che ve l'ho

detto io!)

minimizzare i costi/tempi del setup

minimizzare il "makespan" (la massima lunghezza temporale o l'elapsed time

totale)

Per risolvere, nel miglior modo possibile, un tale problema sono possibili varie

strategie:

algoritmi tipici della teoria dello scheduling

algoritmi legati ai grafi

Un algoritmo tipico della teoria dello scheduling è quello MST (Minimum Spanning Tree) o minimo albero ricoprente. Nel MST il problema è definito nel seguente modo:

sia dato un grafo orientato pesato G(N, A), con un insieme di interi senza segno

definito sugli archi, allora l'obiettivo è quello di determinare una struttura che

connetta tutti i nodi del grafo con archi di peso minimo.

Un albero ricoprente di costo minimo è tipicamente una struttura con un unico nodo

origine, detto radice, ed una serie di rami; il "cammino" da individuare è composto

dall'insieme dei nodi in una sequenza tale che la somma dei pesi sui rami sia la minima

possibile.

E' evidente che l'MST ci porta a minimizzare il "makespan". Poichè abbiamo a che fare

con un grafo a numero di nodi fissi, (ricordate cosa abbiamo detto circa il problema di

Steiner?) allora è possibile usare l'algoritmo di Kruskal (vi rimando al blog precedente

per tale algoritmo).

Ma dobbiamo aprire una parentesi sui labirinti per comprendere il punto 2 precedente

e concludere il discorso sullo scheduling. Quindi c'è un interrupt e salvate un attimo il

contesto sullo stack.

Come vedremo le tecniche di ottimizzazione congiunte spesso vengono utilizzate per

risolvere problemi complessi che non ricadono in una branca specifica

dell'ottimizzazione.

Un tipico algoritmo per trattare i labirinti è l'algoritmo di Tremaux. Ne esistono

Page 3: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

anche altri per la robotica, euristici e random (vedi Wikipedia) ma qui è sufficiente

quello classico.

L'algoritmo di Tremaux consiste nel seguire un percorso, scelto a caso all'interno del

labirinto, fino a raggiungere un incrocio, marcando la via che è stata percorsa fino a

quel momento (nel caso in cui il corridoio conduca a un vicolo cieco è necessario

tornare indietro fino all'incrocio precedente, marcando la via all'andata e al ritorno).

Quando si giunge a un incrocio di più corridoi si prende preferibilmente una via che

non è stata segnata come percorsa in precedenza, e se ciò non è possibile si prende

una via percorsa una sola volta. In ogni caso non è permesso scegliere una via che è

stata già marcata due volte. Iterando il procedimento per ogni incrocio che si trova

sul proprio percorso, l'algoritmo permette di raggiungere l'uscita (o se il labirinto non

ha altre uscite oltre a quella imboccata per entrare, di tornare all'entrata).

La "tecnica di backtracking" è una tecnica euristica generale utilizzabile nei grafi e

negli alberi; serve a tracciarsi i percorsi e durante esso appena si ottiene una

informazione che il cammino fatto è peggiore di un altro si scarta e si ritorna indietro

per un ramo non percorso. Alla fine si rimane col miglior percorso tracciato.

Se ci riflettiamo, una pianificazione assomiglia un po’ all'attraversamento di un

labirinto, dove ci sono un ingresso/inizio ed una uscita/fine, e dove occorre scegliere i

percorsi e marcandoli, in che direzione li percorriamo (grafo orientato) e se una o due

volte.

Il percorso o cammino in effetti è l'insieme dei rami/task da scegliere da legare

insieme; mentre lungo il ramo del percorso troviamo degli oggetti di un certo peso da

mettere in uno zaino, che potrebbero essere anche dei job da fare per ottenere un

oggetto finito.

Mi direte ma nel labirinto non c'è parallelismo ... non è detto: dipende da quante

persone lo percorrono contemporaneamente.

E' come se il problema dello scheduling fosse riconducibile a vari sottoproblemi:

teoria dei grafi e dei labirinti

algoritmi della teoria scheduling

il problema dello zaino

In effetti di un labirinto, almeno inizialmente, nessuno ne conosce i percorsi, così

come nelle pianificazioni che occorre studiare bene come ottenere una pianificazione

decente e coerente.

Page 4: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

Da quanto detto un labirinto è rappresentabile con un grafo orientato ed in esso

occorre:

individuare il percorso

individuare gli elementi secondo una tecnica di backtracking

In particolare durante l'attraversamento di un labirinto ci si segna i nodi e gli archi

orientati e il relativo peso.

Una volta individuato il labirinto occorre iniziare a vedere quale oggetto/task occorre

estrarre per arrivare alla successiva lavorazione. Occorre stare attenti a semplificare

il problema altrimenti si arriva ad aggiungere molte condizioni e vincoli che aumentano

il costo dell'elaborazione. La strategia di scelta degli oggetti/task deve essere,

difatti, quella che è in relazione alla sequenza ottima che minimizza il makespan, il

peso degli oggetti e i tempi di computazione.

Non si può usare prima la tecnica dello zaino e poi la tecnica del MST perché si

arriverebbe ad una soluzione non necessariamente la migliore in termini di

schedulazione; per cui la soluzione migliore è di fare prima un algoritmo che

restituisca il makespan a cui si applica la tecnica dello zaino.

Supponiamo che ci siano n task che m macchine possono lavorare in parallelo, col

vincolo di preemption, e che un task startato su una macchina può continuare su una

macchina differente, a seguito di interruzione sulla macchina precedente.

Chiamiamo Cmax il tempo massimo di lavorazione in cui le m macchine devono

completare gli n task:

Cmax = max(1<=j<=n) Cj

Mentre xij= frazione di task j lavorata sulla macchina i, col vincolo che Sum(i=1,m, xji)

= 1 (Che esprime che una sola macchina per volta lavora un task: non è consentita la

preemption). In particolare xji={0,1}.

Il tempo totale di lavoro della macchina i è: ki=Sum(j=1,n,pj*xji).

Riassumendo la formulazione in LP è:

min Cmax

Sum(j=1,n,pj*xji) <= Cmax

Sum(i=1,m, xij) = 1

Page 5: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

xji>=0

E' vera la seguente affermazione: Sia x* una soluzione che soddisfa tutte le

condizioni e vincoli, allora x* è la soluzione ottima.

Infatti rispetto a x* tutte le macchine hanno lo stesso carico e terminano in Cmax. Se

x* non fosse la soluzione ottima e le macchine lavorassero con carico sbilanciato,

significa che si potrebbe ridurre lo stesso il makespan ridistribuendo il lavoro sulle

varie macchine il carico che era maggiore su una delle macchine. Ma questo ci

riporterebbe a dire che x* non era ottimo ma che ne esiste un altro valore ottimo (ma

che soprattutto non erano soddisfatte bene tutte le condizioni).

In particolare Cmax(x*) = 1/m * Sum(j=1,m,pj). Quindi siamo alla ricerca di un vettore

x* delle variabili decisionali e una volta trovato x* si può passare a studiare il

problema del sacco (knapsack).

Il problema, come già più volte evidenziato, può essere formulato nel seguente modo.

Si hanno a disposizione n oggetti, ciascuno con valore vi e costo ci; nella fattispecie,

tali parametri possono essere rivisti come tempo di processamento e peso. In questo

caso il problema può essere espresso come massimizzazione del valore, nel rispetto

della sequenza makespan e minimizzazione del peso, visto che dovranno essere

effettuati diversi viaggi.

Se C è la disponibilità massima del trasporto, si avrà:

max F = Sum(i=1,n,vi*xi)

Sum(i=1,n,ci*xi)<=C con xi={0,1}

La programmazione dinamica si può espletare nel seguente modo:

determinare il cammino e la lista di oggetti;

implementare il makespan e determinare la sequenza di schedulazione;

entrare nel labirinto e con la procedura di knapsack riempire il sacco, uscire ed

avviare il processamento; mentre ciò avviene, rientrare nel labirinto e prelevare

un altro sottoinsieme di oggetti fintantoché tutti gli oggetti non sono stati

rimossi dal labirinto stesso.

Un'ulteriore complicazione può essere data dall'inserimento nel vincolo di rispetto

della sequenza, e che negli intervalli di tempo in cui l'operatore sta asportando gli

oggetti dal labirinto, almeno un processamento sia in atto; cioè l'insieme delle

Page 6: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

macchine non deve mai essere inoperoso. Chiaramente tale fatto complica

notevolmente la soluzione del problema.

Per fissare i concetti di sopra vediamo però un esempio concreto in LP di una

pianificazione di task e risorse umane, potrebbe essere anche un orario scolastico ad

esempio.

Si possono risolvere problemi molto complessi (in questi ambiti basta poco per

complicarli) e realizzare sistemi con Database Oracle, PL/SQL e con un motore

risolutore GPLK. Nel seguito esamineremo un problemino di schedulazione semplice.

Problema di schedulazione

In un impianto di produzione di tutti i giorni un certo numero di personale è

necessario. Il personale può essere essere assunto per un minimo fino ad un numero

massimo di giorni consecutivi, richiede un numero minimo di giorni di ferie prima di

poter essere nuovamente utilizzati. Il compito è di trovare

l'orario di lavoro riducendo al minimo salario totale da pagare.

Ecco come formuliamo il problemqa in CMLP:

# Personnel assignment problem

#

#

# workload (day, workload)

set L, dimen 2;

# workers (name, min workdays, max workdays, minimum leave, wage)

set W, dimen 5;

# names of workers

set V := setof{(v, b, c, d, e) in W} v;

# periods

set B := ( (min{(b,c) in L} b) .. (max{(b,c) in L} b) );

# minimum number of workdays in row

param ri{v in V} := min{(v, b, c, d, e) in W} b;

# maximum number of workdays in row

param ra{v in V} := min{(v, b, c, d, e) in W} c;

Page 7: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

# minimum leave days in row

param ml{v in V} := min{(v, b, c, d, e) in W} d;

# wage

param wa{v in V} := min{(v, b, c, d, e) in W} e;

# work offer [worker, start, duration]

# (for large problems consider column generation to create work offers)

set O := setof{ v in V, s in B, d in {ri[v]..ra[v]}}( v, s, d);

# workday

param wd{b in B, (v, s, d) in O} :=

if b < s then 0 else if b - s > d - 1 then 0 else 1;

# leaveday

param ld{b in B, (v, s, d) in O} :=

if b < s + d then 0 else if b - s > d + ml[v] - 1 then 0 else 1;

# work offer used

var x{(v,s,d) in O}, binary;

# minimze total wage

minimize wage :

sum{b in B, (v,s,d) in O} x[v,s,d] * wd[b,v,s,d] * wa[v];

# worker can do one job only

s.t. j1{b in B, v in V} :

sum{(v,s,d) in O} x[v,s,d] * ( wd[b,v,s,d] + ld[b,v,s,d] ) <= 1;

# do all jobs

s.t. ja{b in B} :

sum{(v,s,d) in O} x[v,s,d] * wd[b,v,s,d] >= sum{(b, w) in L} w;

solve;

# output solution

printf "\n%-10s", "Day";

for {v in V}

printf "| %-10s", v;

printf "\n";

printf "%-10s", '----------';

for {v in V}

Page 8: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

printf "+-%-10s", '----------';

printf "\n";

for {b in B}

{

printf "%9i ", b;

for {v in V}

printf "| %-9s ",

if sum{(v,s,d) in O} x[v,s,d] * wd[b,v,s,d] then "working"

else "on leave";

printf "\n";

}

printf "\n";

data;

# workload

set L := # day, workload

( 1, 3)

( 2, 1)

( 3, 1)

( 4, 1)

( 5, 1)

( 6, 2)

( 7, 2)

( 8, 2)

( 9, 3)

(10, 2)

(11, 2)

(12, 2)

(14, 1)

(15, 2)

(17, 3)

(18, 3)

(19, 2)

(21, 3)

Page 9: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

(23, 1)

(24, 3)

(25, 3)

(26, 3)

(27, 2)

(28, 1)

(29, 1)

(30, 2)

(31, 1)

(32, 3)

(33, 2)

(35, 3)

(36, 3)

(37, 1)

(38, 3)

(39, 2)

(40, 3)

(43, 1)

(44, 1)

(45, 2)

(46, 2)

(47, 3)

(48, 2)

(49, 1)

(50, 2);

# workers

set W := # name, min workdays, max workdays, minimum leave, wage

( 'Anna', 5, 8, 2, 470 )

( 'Isabel', 5, 8, 2, 500 )

( 'Jack', 1, 8, 2, 1000 )

( 'John', 5, 8, 2, 600 )

Page 10: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

( 'Lisa', 3, 5, 3, 640 );

end;

Ora eseguiamo il file scritto.

K:\Work\LP>glpsol --model workload.txt

Reading model section from workload.txt...

Reading data section from workload.txt...

147 lines were read

Generating wage...

Generating j1...

Generating ja...

Model has been successfully generated

ipp_basic_tech: 8 row(s) and 8 column(s) removed

ipp_reduce_bnds: 1 pass(es) made, 0 bound(s) reduced

ipp_basic_tech: 0 row(s) and 0 column(s) removed

ipp_reduce_coef: 1 pass(es) made, 0 coefficient(s) reduced

glp_intopt: presolved MIP has 293 rows, 1142 columns, 13200 non-zeros

glp_intopt: 1142 integer columns, all of which are binary

Scaling...

A: min|aij| = 1.000e+000 max|aij| = 1.000e+000 ratio = 1.000e+000

Problem data seem to be well scaled

Crashing...

Size of triangular part = 293

Solving LP relaxation...

0: obj = 0.000000000e+000 infeas = 8.700e+001 (0)

* 100: obj = 8.699000000e+004 infeas = 0.000e+000 (0)

* 200: obj = 5.981000000e+004 infeas = 6.106e-016 (0)

* 338: obj = 5.253000000e+004 infeas = 0.000e+000 (0)

OPTIMAL SOLUTION FOUND

Integer optimization begins...

+ 338: mip = not found yet >= -inf (1; 0)

+ 361: >>>>> 5.263000000e+004 >= 5.254000000e+004 0.2% (3; 0)

+ 371: mip = 5.263000000e+004 >= tree is empty 0.0% (0; 5)

INTEGER OPTIMAL SOLUTION FOUND

Time used: 0.2 secs

Memory used: 20.6 Mb (21554667 bytes)

Day | Anna | Isabel | Jack | John | Lisa

----------+-----------+-----------+-----------+-----------+-----------

1 | on leave | working | working | on leave | working

2 | on leave | working | on leave | on leave | working

3 | on leave | working | on leave | on leave | working

4 | on leave | working | on leave | on leave | on leave

5 | on leave | working | on leave | on leave | on leave

6 | working | working | on leave | on leave | on leave

7 | working | working | on leave | on leave | on leave

8 | working | on leave | on leave | working | on leave

9 | working | on leave | working | working | on leave

10 | working | on leave | on leave | working | on leave

11 | working | on leave | on leave | working | on leave

12 | working | on leave | on leave | working | on leave

13 | on leave | on leave | on leave | on leave | on leave

14 | on leave | working | on leave | on leave | on leave

15 | working | working | on leave | on leave | on leave

16 | working | working | on leave | on leave | on leave

17 | working | working | on leave | on leave | working

18 | working | working | on leave | on leave | working

19 | working | on leave | on leave | on leave | working

20 | working | on leave | on leave | on leave | on leave

Page 11: Programmazione Lineare – Schedulazioni vincenti, labirinti tenebrosi, con il bastone da maresciallo nello zaino

21 | working | working | working | on leave | on leave

22 | on leave | working | on leave | on leave | on leave

23 | on leave | working | on leave | on leave | on leave

24 | working | working | on leave | on leave | working

25 | working | working | on leave | on leave | working

26 | working | working | on leave | on leave | working

27 | working | working | on leave | on leave | on leave

28 | working | on leave | on leave | on leave | on leave

29 | working | on leave | on leave | on leave | on leave

30 | working | on leave | on leave | on leave | working

31 | on leave | on leave | on leave | on leave | working

32 | on leave | working | on leave | working | working

33 | on leave | working | on leave | working | on leave

34 | on leave | working | on leave | working | on leave

35 | working | working | on leave | working | on leave

36 | working | working | on leave | working | on leave

37 | working | working | on leave | on leave | on leave

38 | working | working | on leave | on leave | working

39 | working | on leave | on leave | on leave | working

40 | working | on leave | working | on leave | working

41 | on leave | on leave | on leave | on leave | on leave

42 | on leave | on leave | on leave | on leave | on leave

43 | working | on leave | on leave | on leave | on leave

44 | working | on leave | on leave | on leave | on leave

45 | working | on leave | on leave | on leave | working

46 | working | on leave | on leave | on leave | working

47 | working | working | on leave | on leave | working

48 | working | working | on leave | on leave | on leave

49 | on leave | working | on leave | on leave | on leave

50 | on leave | working | on leave | working | on leave

Model has been successfully processed

Un consiglio. E' ovvio che lo strumento vi calcola il tutto in base ai dati che gli avete

fornito e funziona benissimo quando la durata del tempo è ben fissata da regole, come

nell'organizzazione di orari, turni etc.

Mentre nelle pianificazioni di lavori come realizzazione di un software, molto della

pianificazione dipende da voi nel saper ben valutare il tempo, le persone disponibili e

l'organizzazione e professionalità necessarie per svolgere un task, etc. Per questo vi

affiderete a statistiche che avete raccolto nel tempo nell'effettuare quel lavoro o a

metodologie specifiche o a regolamentazioni previste per quel lavoro.