reti neurali di kohonen: un esempio di applicazione al tsp tsp.pdfle reti neurali artificiali sono...

21
Reti neurali di Kohonen: un esempio di applicazione al TSP Stefano Vaccari 2148 061247 Relazione per il corso di Intelligenza Artificiale Febbraio 2003 1 Reti neurali Le reti neurali artificiali sono un insieme di modelli matematici che emulano alcune delle propriet` a dei sistemi nervosi biologici. Questo metodo di elaborazione delle informazioni ` e stato introdotto nel 1943 da W. S. McCulloch e W. Pitts, che per primi studiarono il comporta- mento di una semplice rete neurale da un punto di vista formale. Si pu` o pensare ad una rete neurale come ad un processore distribuito formato da unit` a computazionali elementari interconnesse. Queste unit` a (i neuroni ) hanno due caratteristiche fondamentali: la conoscenza ` e acquisita dall’ambiente attraverso un processo di “ap- prendimento” la conoscenza ` e immagazzinata nei parametri della rete, ovvero nei “pesi” associati alle connessioni 1.1 Modello di neurone McCulloch e Pitts suggerirono la descrizione di un neurone come un elemento a soglia logica con due possibili stati. Esso riceveva gli ingressi (valori 0/1) da N canali e ricavava lo stato dalla loro somma pesata, confrontandola con una soglia predefinita. Se la somma eccedeva quel valore, il neurone si considerava eccitato e l’uscita prendeva il valore ‘1’. I segnali di ingresso erano modulati da costanti dette “forze sinottiche”, del valore di ±1. 1

Upload: others

Post on 04-Mar-2020

15 views

Category:

Documents


1 download

TRANSCRIPT

Reti neurali di Kohonen:un esempio di applicazione al TSP

Stefano Vaccari2148 061247

Relazione per il corso di Intelligenza Artificiale

Febbraio 2003

1 Reti neurali

Le reti neurali artificiali sono un insieme di modelli matematici che emulanoalcune delle proprieta dei sistemi nervosi biologici.

Questo metodo di elaborazione delle informazioni e stato introdotto nel1943 da W. S. McCulloch e W. Pitts, che per primi studiarono il comporta-mento di una semplice rete neurale da un punto di vista formale.

Si puo pensare ad una rete neurale come ad un processore distribuitoformato da unita computazionali elementari interconnesse. Queste unita (ineuroni) hanno due caratteristiche fondamentali:

• la conoscenza e acquisita dall’ambiente attraverso un processo di “ap-prendimento”

• la conoscenza e immagazzinata nei parametri della rete, ovvero nei“pesi” associati alle connessioni

1.1 Modello di neurone

McCulloch e Pitts suggerirono la descrizione di un neurone come un elementoa soglia logica con due possibili stati. Esso riceveva gli ingressi (valori 0/1)da N canali e ricavava lo stato dalla loro somma pesata, confrontandolacon una soglia predefinita. Se la somma eccedeva quel valore, il neurone siconsiderava eccitato e l’uscita prendeva il valore ‘1’.

I segnali di ingresso erano modulati da costanti dette “forze sinottiche”,del valore di ±1.

1

McCulloch e Pitts dimostrarono che la combinazione di questi neuronipermetteva di ricavare l’espressione di una qualsiasi funzione logica, accop-piandoli in parallelo ed in strati successivi.

I due matematici pero non specificarono come dovevano essere eseguitele connessioni, e come le soglie e i pesi della sommatoria dovevano essereconfigurati attraverso l’apprendimento.

Il neurone usato attualmente deriva dal precedente, ampliando le suefunzionalita. Gli ingressi vengono moltiplicati per i pesi (valori continui,questa volta) e poi sommati; l’uscita del neurone dipende dalla soglia diattivazione e dalla funzione di trasferimento, nuovo parametro del sistema.

Le funzioni di trasferimento piu usate sono:

• a gradino

• a rampa

• sigmoidea

Ad esempio, usando la funzione a gradino si puo descrivere il modello diMcCulloch e Pitts, che ha:

• uscita ‘1’ se la somma pesata degli ingressi supera una soglia φ

• uscita ‘0’ altrimenti

Ovvero:

y = h

(N∑

i=1

wixi − φ

)

dove:

• y ∈ {0, 1} uscita

• xi ∈ R ingressi

2

• wi ∈ R pesi

• N numero di ingressi

• φ ∈ R soglia

• h(t) : R → R funzione di attivazione:

h(t) =

{+1 t ≥ 00 t < 0

1.2 Il perceptron

Una rete costituita da due strati di neuroni e stata denominata perceptron(F. Rosenblatt, 1962).

Il primo strato si occupa degli ingressi (binari) della rete, il secondo delleuscite. Una matrice W(N×M) ∈ R di pesi collega ogni neurone del primostrato con quelli del secondo.

Per funzionare, la rete deve essere “istruita” attraverso un algoritmo peril calcolo dei pesi, noto come perceptron learning rule, per poter produrreun’uscita corretta dato un qualsiasi ingresso.

Questa regola di apprendimento puo essere descritta con l’equazione:

Wi,j(t) = Wi,j(t− 1) + µ · (Tj − Aj)pi(t)

{∀ i = 1...N∀ j = 1...M

(1)

3

dove:

• Tj ∈ {0, 1} uscita desiderata del neurone j

• Aj ∈ R uscita attuale del neurone j

• pi ∈ {0, 1} vettore degli ingressi

• Wi,j ∈ R matrice dei pesi

•{

t stato attualet− 1 stato precedente

• N numero dei neuroni dello strato di ingresso

• M numero dei neuroni dello strato di uscita

• µ ∈ [0...1] coefficiente di apprendimento

I vettori del training set (le combinazioni di ingresso/uscita note) sonopresentati alla rete uno dopo l’altro. Se l’uscita e corretta, non si fannocambiamenti ai parametri. Altrimenti, i pesi e le soglie sono aggiornati conla regola di apprendimento. Si prosegue finche il training non produce errori(o l’errore rimane entro un limite prefissato), ed a quel punto si e pronti adutilizzare la rete con i dati reali.

Se un vettore “nuovo” (non presente nel training set) verra presentatoalla rete, essa attuera una generalizzazione: ovvero rispondera con un’uscitatale da assomigliare a quella che si avrebbe con un vettore di ingresso ap-partenente al training set, simile a quello non ancora visto.

4

Le limitazioni del perceptron sono state messe in luce da Minsky e Papert(“Perceptrons”, 1969), che hanno mostrato l’esistenza di problemi non risolvi-bili da questo modello di rete.

Supponiamo ad esempio di esaminare il comporta-mento di un particolare perceptron con due ingressi,rappresentando gli input sugli assi di un piano carte-siano: vediamo che questa rete e in grado di discrimi-nare i segnali di uscita separandoli con una linea (adesempio separando gli “0” dagli “1”).

Se una linea (o un iperpiano, in generale) puo sepa-rare le classi di uscita, allora il problema e linearmenteseparabile e il perceptron trovera una soluzione in untempo finito.

Nel caso questo non sia possibile, il sistema nonraggiungera mai una soluzione stabile nella quale leclassi siano classificate propriamente.

In figura possiamo vedere alcuni esempi:

Quando e stata dimostrata l’esistenza di questo limite, le ricerche sullereti neurali hanno subıto un freno. Sono comunque proseguite, portando almodello descritto nel prossimo paragrafo.

1.3 Multilayer perceptron

Un multilayer perceptron e un esempio di rete con uno o piu strati di neu-roni tra i nodi input e quelli output (unita nascoste o hidden units). Lostrato nascosto riceve gli ingressi dallo strato precedente, e dipendentementedai pesi produce un’uscita che prosegue nello strato successivo. Per quantoriguarda il numero di nodi da inserire, in linea generale, i neuroni dello stratonascosto devono essere almeno quante sono le classi di input presentate du-rante l’apprendimento.

Ogni neurone ha un’uscita non lineare continua, data dalla funzione sig-moidea: essa e necessaria per correggere gli errori durante l’apprendimento.

5

L’analisi delle reti multi-strato ha mostrato che non sono necessari piu ditre strati perche una rete di questo tipo sia in grado di riconoscere una qual-siasi regione di uno spazio n-dimensionale (teorema di Kolmogorov). Ognineurone e, infatti, in grado di individuare due semispazi, per cui intersecandotali semispazi si possono ottenere altrettante regioni separate.

Benche gia dagli anni sessanta si sapesse del vantaggio offerto da retimulti-strato non lineari, non esisteva ancora un algoritmo di apprendimentoche fosse in grado di calcolare il contributo delle unita interne alla funzionedi errore misurata sulle unita di output.

L’aumento delle potenze di calcolo degli elaboratori risveglio l’interesse,negli anni ottanta, verso queste reti. Si riuscı infatti ad applicare con successol’algoritmo di apprendimento del tipo “backpropagation”, ancora oggi uno deimetodi piu utilizzati.

1.4 Apprendimento

L’apprendimento e il processo mediante il quale vengono determinati i para-metri di una rete. Si puo distinguere tra apprendimento “supervisionato” eapprendimento “non supervisionato”.

Nel primo caso i parametri vengono costruiti sulla base di un insieme diesempi (il training set) consistenti in coppie di ingresso/uscita desiderate. Loscopo dell’addestramento e quello di costruire un modello del processo chegenera i dati.

Nel secondo caso la rete e dotata di una capacita di auto-organizzazioneed e in grado di classificare gli ingressi autonomamente. E il caso delle retineurali di Kohonen, che verranno analizzate nel seguito.

6

2 Reti di Kohonen

Nell’esempio del perceptron, e nelle reti da esso derivate, era necessarial’implementazione di un insegnante che sapesse a priori la classificazione deipattern di input nel training set. L’obiettivo era quindi la generalizzazioneda questi ad altri input mai analizzati esplicitamente, lasciando il compitoalla rete di decidere la risposta piu corretta.

Nelle reti di tipo non supervisionato, si lascia il compito alla rete stessadi ricercare una classificazione logica dei dati di ingresso.

Le Reti di Kohonen (T. Kohonen, 1983) nascono dallo studio della topolo-gia della corteccia del cervello umano. Queste reti tengono conto non solodelle connessioni sinottiche tra neuroni ma anche dell’influenza che puo avereun neurone sul vicino. E stato osservato che, nel caso biologico, i neuroni chesono fisicamente vicini a neuroni attivi hanno i legami piu forti, mentre quelliad una particolare distanza hanno legami inibitori. A questa caratteristicaKohonen attribuisce uno sviluppo nella capacita di realizzare delle mappetopologiche (ovvero che mantengono le relazioni di vicinanza) localizzate nelcervello. Questa caratteristica e stata modellata da Kohonen restringendola variazione dei pesi soltanto ai neuroni giudicati “vicini” ad un neuronescelto.

2.1 Mappe autoorganizzanti

Una rete di Kohonen e costituita da una serie di neuroni di input (come perle reti multi-strato) e da una griglia n-dimensionale 1 di neuroni. Ciascunneurone di input e connesso a tutti i neuroni della griglia: la risultante ma-trice dei pesi viene usata per propagare l’ingresso della rete ai neuroni dellamappa.

Inoltre, tutti i neuroni della mappa sono connessi tra loro. Queste con-nessioni servono per influenzare i neuroni adiacenti al neurone con la piu altaattivazione, dipendentemente dalla distanza da esso.

1n rappresenta il numero di attributi usati nella classificazione dei segnali di ingresso.

7

L’interazione tra i neuroni della mappa porta alla modifica dei relativipesi, in base ad una funzione che determina l’influenza dei nodi vicini. Sipuo usare per questo scopo la funzione di Gauss con un parametro σ chiamatovicinanza:

G(i, k) = e−|wk−wi|

2

2σ2 (2)

In principio, il parametro σ e alto e quindi l’area di interazione e grande;man mano che l’apprendimento continua l’area di attivazione e costantementediminuita, in modo che soltanto i neuroni vicini a quello piu attivato sianoinfluenzati e ci sia convergenza dell’algoritmo.

Scelto il neurone i con la piu alta attivazione, ovvero quello che e piuvicino all’ingresso attuale:

|wi − T | ≤ |wk − T | ∀k = 1...N (3)

i pesi verranno aggiornati in questo modo:

wk(t) = wk(t− 1) + µ ·G(i, k)(T − wk(t− 1)) ∀k = 1...N (4)

• µ ∈ [0...1] coefficiente di apprendimento

• T ∈ R input da classificare

• N numero di neuroni della mappa

•{

t stato attualet− 1 stato precedente

2.2 Algoritmo

Il ciclo che definisce l’apprendimento delle reti neurali di Kohonen si puoquindi schematizzare in questo modo:

1. Definire l’area di attivazione iniziale (σ)

2. Inizializzare i pesi della mappa (casualmente)

3. Passare un dato qualsiasi allo strato di input

4. Determinare il neurone piu attivato nella mappa

5. Aggiornare i pesi della mappa con la funzione di Gauss

8

6. Diminuire l’area di attivazione (il parametro σ)

7. Ripetere dal passo 3

L’algoritmo termina quando σ diventa minore di un valore prestabilito, oquando i pesi della mappa hanno un valore stabile.

2.3 Possibili applicazioni

L’abilita delle reti non supervisionate di individuare in un certo insieme par-tizioni di dati omogenei, risulta di notevole interesse in numerosi ambiti. Leapplicazioni piu frequenti si trovano nei campi di:

- Data mining

- Analisi economica

- Statistica

- Compressione immagini

- Riconoscimento voce/scrittura

3 Applicazione al TSP

Il problema del commesso viaggiatore (TSP, Traveling Salesman Problem)e un problema classico, e consiste nel trovare, date N citta, il percorso piubreve che le congiunga tutte, ritornando infine alla citta iniziale.

Algoritmi per la risoluzione ottima o approssimata sono stati largamentediscussi in molte pubblicazioni: l’obiettivo di questa analisi del problema at-traverso le reti di Kohonen vuole essere un esempio pratico dell’implementazionedel modello di rete neurale ad un problema reale.

3.1 Il modello del sistema

Come prima cosa occorre decidere quali e quanti attributi assegnare ai dati diingresso, per poter eseguire la classificazione. Si sta esaminando la distanzatra le citta in modo da costruire l’anello piu corto: i neuroni da attivaresaranno scelti in base a questo attributo.

Citta e neuroni avranno nelle loro strutture dati le coordinate (X,Y )necessarie per calcolare la distanza euclidea nel piano di lavoro.

9

La mappa di Kohonen sara 1-dimensionale (rappresenta il percorso); ini-zialmente conterra neuroni equidistanti, che via via correggeranno i loro pesiper adattarsi alle posizioni delle citta di input.

I neuroni di ingresso sono due, uno per la X e uno per la Y : tramite unaserie di pesi ciascuno di essi sara collegato ad ogni neurone della mappa. Si ri-corda infatti che il modello di Kohonen impone che la rete sia completamenteconnessa.

Gli ingressi del sistema saranno proprio le coordinate delle citta, scelteciclicamente per tutta la durata dell’elaborazione.

I nodi della mappa verranno inizialmente disposti come un anello, ed ipesi inizializzati a valori casuali; le coordinate dei neuroni dell’anello noncambieranno durante l’esecuzione: soltanto i pesi (2 per neurone) sarannoaggiornati.

Questi ultimi, grazie alla regola di apprendimento, tenderanno ad as-sumere i valori delle coordinate di input; alla fine dell’algoritmo, dal vettoredi neuroni si ricavera l’ordine di visita delle citta.

Rimane da discutere il numero dei nodi dell’anello. Esso deve esseresuperiore al numero di citta, per permettere l’adattamento della rete. Secosı non fosse, la convergenza del sistema verso una soluzione ottima nonsarebbe garantita, soprattutto con un grande numero di citta.

Il totale di neuroni da utilizzare e stato ricavato in via sperimentale: unbuon compromesso tra precisione dell’algoritmo e velocita di elaborazione sie avuto con un numero doppio di neuroni rispetto alle citta di input.

Come gia detto, i pesi dei neuroni (aggiornati dall’algoritmo) si adatte-ranno ad ogni ciclo alle coordinate delle citta date all’ingresso. Il parametroσ (vicinanza) determinera l’evoluzione del sistema: infatti sotto una certasoglia il percorso non potra cambiare significativamente. Verra deciso quindiun valore che limitera il numero di iterazioni.

10

3.2 Parametri

Il tuning dei parametri si e rivelato fondamentale per la buona riuscitadell’algoritmo. Infatti una scelta sbagliata di σ, ad esempio, avrebbe portatoad un grande numero di iterazioni (se troppo alto) o ad una ‘staticita’ deineuroni (se troppo basso).

Si e effettuata la scelta di avere σ = 0.5 all’inizio dell’algoritmo, e di faredecrescere il suo valore attraverso il momento, impostato a 0.9995, tramitela formula:

σ(t) = σ(t− 1) · momento (5)

fino a quando il suo valore fosse minore di 0.03, un buon compromessotra precisione dell’algoritmo e numero di cicli di elaborazione, fissato (conquesti parametri) a 5600 iterazioni.

Il coefficiente di apprendimento µ si e rivelato altrettanto importante alfine di ottenere migliori risultati; ne e prova il fatto che la lunghezza delpercorso puo aumentare del 4/6% per un µ non ottimale.

La bonta della scelta del parametro si e rivelata in una certa misura dipen-dente dai dati di ingresso. La migliore soluzione si e avuta con µ posizionatotra i valori 0.5 e 0.7, una zona contenuta che permette con 3 tentativi diottenere il valore ottimo da utilizzare.

Anch’esso viene aggiornato durante l’elaborazione, servendosi della for-mula:

µ(t) = µ(t− 1) · momento (6)

11

Naturalmente sarebbe possibile adattare i parametri dinamicamente, inbase al numero di citta ed alla precisione voluta.

3.3 Risultati

I problemi applicati alla rete neurale sono stati scelti dalla libreria TSPLIB,che contiene decine di istanze di TSP e le relative soluzioni ottime. In par-ticolare sono state scelte:

• ulysses22

• att48

• eil76

• ch150

I parametri di velocita e precisione dell’algoritmo sono stati fissati pertutte le istanze a:

momento = 0.9995 σlimite = 0.03

Questi sono i risultati ottenuti:

OPT Kohonen % Tempo (s)ulysses22 75.67 79.16 +4.6 2.3

att48 33524 37160 +10.8 10.7eil76 549.8 589.6 +7.2 27.5ch150 6528.8 7469.6 +14.4 120

Tab.1: µ = 0.5 - tempi per un processore x86 a 700 Mhz

OPT Kohonen % µ ottimoulysses22 75.67 76.8 +1.5 0.6

att48 33524 35692 +6.5 0.7eil76 549.8 589.6 +7.2 0.5ch150 6528.8 7086.6 +8.5 0.7

Tab.2: risultati con µ ottimo

12

Si nota che gia dall’iterazione 4000 i neuroni sono disposti sul percorso ottimale;la precisione e eccessiva.

13

14

15

Molte citta non sono state raggiunte dai neuroni: sarebbero necessarie altreiterazioni per aumentare la precisione.

16

3.4 Conclusioni

Come era lecito aspettarsi, le reti di Kohonen non sono certo il miglioremetodo risolutivo per il TSP. Esse vengono infatti battute dagli algoritmidi programmazione lineare (es: 2-Opt, 3-Opt) su ogni istanza, sia comeprecisione sia come tempi di soluzione.

L’algoritmo base, qui applicato, mantiene un margine dall’ottimo entro il9% per tutte le istanze, migliorabile aumentando la precisione dell’algoritmo.

Il tempo di elaborazione e funzione quasi esponenziale della grandezzadell’istanza, ma il codice lascia spazio a molti tipi di ottimizzazione (soprat-tutto nei calcoli floating point).

Certamente si possono aggiungere estensioni all’algoritmo base: ad esem-pio una variante consisterebbe nella duplicazione del nodo vincente, aggiun-gendo un neurone tra i suoi vertici vicini nell’anello. Contemporaneamente sipotrebbe togliere uno dei neuroni che nelle ultime iterazioni non e mai statoattivato.

Fritzke e Wilke2 hanno applicato questa versione dell’algoritmo (con al-cune migliorie) per risolvere istanze di TSPLIB fino a pr2392.

Favata e Walker3 sono riusciti a risolvere istanze fino a 10000 citta, ma laqualita dei loro percorsi decadeva con il crescere della dimensione dell’istanza.

Infine Amin4 ha risolto un’istanza da 30000 citta, ma in un tempo di44 ore di calcolo (su una workstation) quando gli algoritmi 2- o 3-Opt cimettevano meno di 20 secondi.

Da tutto questo possiamo concludere che le reti di Kohonen, applicateal TSP, non hanno vantaggi rispetto agli algoritmi di programmazione line-are. Come gia detto esse hanno campi di applicazione ben diversi, nei qualipossono mostrare tutte le loro potenzialita.

2FLEXMAP: A neural network for the traveling salesman problem with linear timeand space complexity, 1991

3A study of the application of Kohonen-type neural networks to the travelling salesmanproblem, 1991

4A self-organized travelling salesman, 1994

17

4 Appendice - il codice

In questa sezione verranno spiegate le procedure usate e le altre scelte diprogetto. Per l’implementazione e stato scelto il linguaggio C.

• void matr dist();

Inizializza la matrice delle distanze tra i neuroni; la matrice e costante,in quanto l’anello di neuroni non cambia posizione nella mappa.

• void calcola gauss(double distanza);

Utilizzando la formula:

G(i, j) = e−|wi−wj |

2

2σ2 (7)

Calcola quanto un neurone influisca su quelli vicini, in base alla distanza.Il numeratore dell’esponente e memorizzato nella matrice

mdist[i][j] = −|wi − wk|2 (8)

che viene inizializzata dall’algoritmo.

• int nearest(double x, double y);

Restituisce l’indice del neurone piu vicino rispetto ai parametri passatialla funzione.

• void aggiorna pesi(int j, double x, double y);

Il “cuore” dell’algoritmo: questa procedura implementa la regola di ap-prendimento:

wk(t) = wk(t− 1) + µ ·G(i, k)(T − wk(t− 1)) ∀k = 1...N (9)

18

• void stampa percorso();

Questa procedura e fondamentale per ricavare il percorso dai valori deipesi dei neuroni. Essa assegna ad ogni citta il neurone piu vicino. Se duecitta sono pero vicine allo stesso neurone, quest’ultimo potrebbe essere giastato scelto; viene allora cercato il nodo migliore, in base alla distanza, nonancora assegnato.

Infine il percorso risultante viene salvato su file e la lunghezza totalestampata a video.

• void random city();

Se il programma e eseguito senza parametri di ingresso, vengono createQUANTE RANDOM citta casuali con coordinate normalizzate tra 0 e 1.

• void load city();

Carica i dati da un file di testo, nel formato tipico di TSPLIB ovvero:n X Y

Es.:1 48.24 30.42

2 49.57 36.15

3 50.56 35.32

Le coordinate vengono salvate nei relativi vettori normalizzate tra i valori[0...1].

• void init();

E la procedura che inizializza:

- pesi neuroni → tra [0...1], casuali

- coordinate neuroni → ad anello

- matrice distanze → con matr dist();

- matrice gauss → con calcola gauss(sigma);

19

• void salva(int val);

Salva i passi di esecuzione (val: numero del passo) su disco nel fileNEURONI.TXT, in formato testuale.

• int main(int argc, char *argv[]);

La sintassi del programma comprende:

- TSP → senza parametri: elabora QUANTE RANDOM citta casuali

- TSP file.txt → carica le citta dal file specificato

- TSP file.txt .5 → specifica il valore base di µ

Il codice sorgente, ed i file di dati utilizzati nel presente documento, sipossono trovare al seguente indirizzo:

http://digilander.libero.it/dasga/kohonen.html

Per informazioni su questa relazione, scrivere a:

[email protected]

Made with LATEX2ε

20

Contents

1 Reti neurali 11.1 Modello di neurone . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Il perceptron . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.3 Multilayer perceptron . . . . . . . . . . . . . . . . . . . . . . . 51.4 Apprendimento . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Reti di Kohonen 72.1 Mappe autoorganizzanti . . . . . . . . . . . . . . . . . . . . . 72.2 Algoritmo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.3 Possibili applicazioni . . . . . . . . . . . . . . . . . . . . . . . 9

3 Applicazione al TSP 93.1 Il modello del sistema . . . . . . . . . . . . . . . . . . . . . . . 93.2 Parametri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.3 Risultati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.4 Conclusioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Appendice - il codice 18

21