Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
1
Il presente quaderno di appunti e stato redatto completamente con l’applicativo TEXnicCenter
Indice
I Teoria 3
1 Gli Algortmi 5
1.1 Problemi Decisionali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.1 Problemi trattabili/intrattabili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Ricerche su i vettori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.1 Ricerca lineare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.2 Ricerca binaria o dicotomica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Algoritmi di ordinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3.1 Classificazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3.2 Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.3 Bubble Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3.4 Selection Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.5 Counting Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.4 Analisi della complessità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4.1 Classificazione degli algoritmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4.2 Analisi asintotica di capo peggiore . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4.3 Notazione asintotica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.5 Online Connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.5.1 Quick Find . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5.2 Quick Union . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.6 Matematica discreta: grafi e alberi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6.1 Grafo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6.2 Incidenza e adiacenza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.6.3 Grado di un vertice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.6.4 Cammini e raggiungibilità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.6.5 Connessione nei grafi non orientati . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.6.6 Connessione nei grafi orientati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.6.7 Grafi densi o sparsi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.6.8 Grafo pesato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.6.9 Alberi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.7 La ricorsione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.7.1 Paradigma Divide et Impera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.7.2 Analisi di complessità di alcuni algoritmi . . . . . . . . . . . . . . . . . . . . . . . 25
1.7.3 Torri di Hanoi, gioco matematico risolto con il dividi et impera . . . . . . . . . . . 26
1.7.4 Backtracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.8 ADT: Heap, code a priorità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.8.1 Procedura di insert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.8.2 Procedura di heapify . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.8.3 Procedura di BuildHeap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.8.4 HeapSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.9 ADT: Tabella di simboli (Symbol Table) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.9.1 Operazioni di un ADT SystemTable . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.9.2 Strutture dati di un ADT SystemTable . . . . . . . . . . . . . . . . . . . . . . . . 30
1.9.3 Ricerca in un ADT SystemTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.10 Alberi binari di ricerca (BST) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.10.1 Operazioni in un BST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.10.2 Complessità in un BST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.10.3 Operazioni utili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.10.4 Implementazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
I
II Programmazione 35
2 Problem Solving elementare 37
2.1 Problem Solving elementare su dati scalari . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.1.1 Problem Solving e algoritmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.1.2 Strategie di soluzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.1.3 Classificazione dei problemi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
III Laboratorio 53
4 Esercitazione 1 55
4.1 Esercizio n. 1: manipolazione di una matrice - I . . . . . . . . . . . . . . . . . . . . . . . . 55
4.2 Esercizio n. 2 - Manipolazione di una matrice - II . . . . . . . . . . . . . . . . . . . . . . . 57
4.3 Esercizio n. 3: riformattazione di un testo . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5 Esercitazione 2 63
5.1 Esercizio n. 1: decompressione di un testo . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.2 Esercizio n. 2: stringhe periodiche. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.3 Esercizio n. 3: voli aerei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
6 Esercitazione 3 69
6.1 Esercizio n. 1: confronto tra algoritmi di ordinamento . . . . . . . . . . . . . . . . . . . . 69
6.2 Esercizio n. 2: ordinamento di stringhe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7 Esercitazione 4 75
7.1 Esercizio n. 1: occorrenze di parole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
7.2 Esercizio n. 2: indice analitico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.3 Esercizio n. 3: prodotto di matrici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8 Esercitazione 5 83
8.1 Esercizio n. 1: Generazione di numeri binari. . . . . . . . . . . . . . . . . . . . . . . . . . 83
8.2 Esercizio n. 2: Sviluppo di un sistema del totocalcio. . . . . . . . . . . . . . . . . . . . . . 84
8.3 Esercizio n. 3: calcolo del determinante di una matrice. . . . . . . . . . . . . . . . . . . . 85
9 Esercitazione 6 89
9.1 Esercizio n. 1: Punti del piano. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.2 Esercizio n. 2: Ricerche dicotomiche. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
9.3 Esercizio n. 3: Confronto tra algoritmi di ordinamento. . . . . . . . . . . . . . . . . . . . 97
10 Esercitazione 7 103
10.1 Esercizio n. 1: gestione di strutture FIFO. . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
10.2 Esercizio n. 2: gestione di strutture LIFO. . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
II
11 Esercitazione 8 117
11.1 Esercizio n. 1: gestione di una coda prioritaria - I . . . . . . . . . . . . . . . . . . . . . . . 117
11.2 Esercizio n. 2: gestione di una coda prioritaria - II . . . . . . . . . . . . . . . . . . . . . . 126
III
IV
Elenco dei Sorgenti
V
6.3 Ordinamento di stringhe con allocazione dinamica . . . . . . . . . . . . . . . . . . . . . . 73
7.1 Occorrenza di parole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
7.2 Occorrenze parole con indice analitico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.3 Prodotto di matrici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.1 Generazione di numeri binari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
8.2 Sviluppo di un sistema del totocalcio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.3 Determinante di una matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.1 Punti del piano (client.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.2 Punti del piano (point.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
9.3 Punti del piano (point.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
9.4 Ricerche dicotomiche (client.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
9.5 Ricerche dicotomiche (item.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
9.6 Ricerche dicotomiche (item.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
9.7 Confronto tra algoritmi di ordinamento (client.c) . . . . . . . . . . . . . . . . . . . . . . . 97
9.8 Confronto tra algoritmi di ordinamento (item.h) . . . . . . . . . . . . . . . . . . . . . . . 98
9.9 Confronto tra algoritmi di ordinamento (item.c) . . . . . . . . . . . . . . . . . . . . . . . . 99
9.10 Confronto tra algoritmi di ordinamento (sort.h) . . . . . . . . . . . . . . . . . . . . . . . . 99
9.11 Confronto tra algoritmi di ordinamento (sort.c) . . . . . . . . . . . . . . . . . . . . . . . . 100
10.1 Queue (client.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
10.2 Queue (item.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.3 Queue (item.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.4 Queue (queue.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.5 Queue (queue.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
10.6 Queue (queue.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10.7 Queue (queue.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10.8 Stack (client.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
10.9 Stack (item.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
10.10Stack (item.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
10.11Stack (stack.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
10.12Stack (stack.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
10.13Stack (stack.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
10.14Stack (stack.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
11.1 Coda prioritaria - I (client.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
11.2 Coda prioritaria - I (job.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.3 Coda prioritaria - I (job.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.4 Coda prioritaria - I (ITEM.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
11.5 Coda prioritaria - I (ITEM.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
11.6 Coda prioritaria - I (HEAP.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
11.7 Coda prioritaria - I (HEAP.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
11.8 Coda prioritaria - I (PriorityQueue.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
11.9 Coda prioritaria - I (PriorityQueue.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
11.10Coda prioritaria - I (LIST.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
11.11Coda prioritaria - I (LIST.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
11.12Coda prioritaria - I (PriorityQueue.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
11.13Coda prioritaria - I (PriorityQueue.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
11.14Coda prioritaria - II (client.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
11.15Coda prioritaria - II (Hour.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
11.16Coda prioritaria - II (Hour.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
11.17Coda prioritaria - II (job.h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
11.18Coda prioritaria - II (job.c) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
Presentazione
Il corso è tenuto dal Prof. Paolo Enrico Camurati, Prof. Giampiero Cabodi e dal Prof. Sergio Nocco.
I laboratori inizieranno nella settimana del 10 ottobre 2011 e sono nelle date:
Squadra 1 Giovedı̀ dalle ore 14:30 alle ore 16:00
Squadra 2 Giovedı̀ dalle ore 16:00 alle ore 15:30
Squadra 3 Venerdı̀ dalle ore 11:30 alle ore 13:00
Squadra 4 Venerdı̀ dalle ore 13:00 alle ore 14:30
I testi consigliati sono:
• Sedgewick Robert, Algoritmi in C, Pearson, ISBN 8871921518
• Deitel & Deitel, Corso completo di programmazione in C, Apogeo, ISBN 8850326335
L’esame consiste in due parti, scritto e orale. Lo scritto comprenderà una parte teorica (12 punti) e una
parte di programmazione (18 punti). Durante lo scritto sarà possibile consultare manuali di C come il
Deitel & Deitel oppure il Kernighan & Ritchie, inoltre, al termine dello scritto bisogna portar copia del
programa per correggerlo e inviarlo corretto al docente responsabile del corso. Il voto dell’esame orale
non farà media in quanto sarà già comprensivo della valutazione dello scritto.
Per consulenze non sono previsti degli orari esatti, ma basta mandare una mail e sarà fissato un appun-
tamento.
1
2
Parte I
Teoria
3
Capitolo 1
Gli Algortmi
Un algoritmo è come una ricetta, è composto da una sequenza finita di istruzioni elementari.
L’algoritmo ha l’obiettivo di risolvere un problema partendo dai dati di input, ottenendo quindi i dati di
output.
Ogni singola istruzione ha un suo significato, che non può variare in seguito.
Un algoritmo, per sua definizione, deve avere un limite superiore, è certo quindi che un algoritmo abbia
una terminazione (anche se non in tempi accettabili).
Si usano gli algoritmi per essere portati all’interno di elaboratori elettronici, in modo da poter aumentare
la velocità e quindi poter anche aumentare la mole di dati da poter trattare in tempi ragionevoli.
Un super-computer comunque non può sostituire un buon algoritmo.
• Vi sono problemi dedicibili definiti come intrattabili, in quanto sono risolubili con algoritmi polino-
miali. Esempi possono essere quelli con complessità esponenziale (come le Torri di Hanoi).
Esistono problemi per cui si conosce il solo algoritmo esponenziale, ma non è mai stato dimostrato
che non esiste un algoritmo polinomiale.
• Si chiama classe NP la classe di problemi che hanno una verifica di tipo polinomiale ma una risolu-
zione di tipo non polinomiale (potrebbe anche esistere un algoritmo di tipo polinomiale ma ancora
non è stato dimostrato che non esiste).
P ⊆ N P per definizione, ma è possibile che P coincide con N P , è probabile che vi siano problemi
N P che non sono P .
5
Esiste un sottoinsieme di N P denominato come NP-Completo, tali che con una serie di trasforma-
zioni si risolvono in modo polinomaile.
Durante il corso si tratteranno solo problemi di classe P .
• se l’elemento non viene trovato viene ritornato un elemento sentinella, che non può essere un indice
i∈/ [0, n − 1] (generalmente −1).
caso migliore 1 accesso
successo caso peggiore n accessi
n Bisogna carattarizzare l’algoritmo con la sua complessità
caso medio 2 accessi
insuccesso n accessi
nel caso peggiore (una stima conservativa).
1 int ri ce rc aS eq uen zi al e ( int v [] , int l , int r , int k )
// l = estremo sinistro , r = estremo destro , k = chiave
3 {
int i = l ;
5 while (i < r && k != v [ i ])
i ++;
7 if ( i == r )
return -1;
9 return i ;
}
Listing 1.1: Ricerca lineare
int c ;
4 while (c <= b )
{
6 c = ( a + b ) /2;
if ( v [ c ]== k )
8 return c ;
else if ( v [ c ] < k )
10 a = c +1;
else
12 b = c -1;
}
14 return -1;
}
6
Listing 1.2: Ricerca binaria
1.3.1 Classificazione
Gli algoritmi di ordinamento possono essere classificati secondo metodo diversi:
ordinamento interno se i dati da ordinare sono tutti in memoria centrale, ha accesso diretto ai dati
ordinamento esterno si i dati da ordinare sono, anche non tutti, in memoria esterna (memoria di
massa), ha accesso sequenziale ai dati
ordinamento in loco se per l’ordinamento oltre al vettore saranno necessarie una quantità finita di
memoria che è indipendente dalla dimensione del vettore
ordinamento non in loco se per l’ordinamento saranno necessarie delle locazioni di memoria e tale
quantità è dipendente dalla dimensione del vettore
ordinamento stabile se in fase di ordinamento se sono presenti più elementi con medesima chiave essi
saranno ordinati ma resteranno con lo stesso ordine con cui erano nel vettore
in base alla complessità :
• O(n2 ) sono gli algoritmi di ordinamento più semplici, iterativi e basati sul confronto
esempio di algoritmi di questa classe è Insersion Sort, Selection Sort, Exchange/Bubble Sort
3
• O(n 2 ) un esempio di questo tipo è lo ShellSort (non verrà considerato nel seguito del corso)
• O(n · log(n)) sono gli algoritmi di ordinamento più complessi, ricorsivi e basati sul confronto
esempio di algoritmi di questa classe è Merge Sort, Quick Sort e Heap Sort
• O(n) sono algoritmi di ordinamento non basati sul confronto, sono applicabili solo con delle
ipotesi molto restrittive, sono basati sul calcolo
esempio di algoritmi di questa classe è Counting Sort, Radix Sort, Bin/Bucket Sort
Gli algoritmo di ordinamento con complessità O(n·log(n)) sono gli algoritmi di ordinamento migliori
nel caso in cui sia necessario il confronto, mentre la migliore complessità si ha con complessita O(n)
ma sono poco utilizzabili perchè richiedono delle ipotesi troppo restrittive.
Dimostrazione 1 (Limite inferiore di complessità negli algoritmi basati sul confronto) Un al-
goritmo di ordinamento basato sul confronto ha come operazione elementare il confronto tra ai e aj , sarà
necessario quindi decidere se ai < aj e ai ≥ aj .
Se volessi rappresentare le decisioni in modo grafico si avrebbe la realizzazione di un albero delle decisioni
(che è un albero binario). Se volessi trovare l’ordinamento dei seguenti elementi a1 , a2 , a3 (supponendo
di conoscere le relazioni tra gli elementi) avrei la realizzazione del seguente albero decisionale
a1:a2
<= >
a2:a3 a1:a3
<= > <= >
7
Nel caso di n dati da ordinare tutte le possibili condizioni di uscita sono tutte le permutazioni che si
possono realizzare con n elementi, cioè n!. Se contassimo il numero di confronti che ci sono tra la radice
e la foglia (permutazione corretta) tale valore è sempre uguale al numero di archi che separano la radice
dalla foglia.
Poichè un albero decisionale è un albero binario, si può supporre che l’albero decisionale sia un albero
binario completo nel quale tutte le permutazioni si possono trovare solo sulle foglie. Detta h l’altezza
massima dell’arco si possono avere al massimo 2n foglie, ma abbiamo supposto che le permutazioni devono
essere sulle foglie, pertanto sarà necessario avere un numero di foglie pari ad almento n!. Si ha quindi
n! ≤ 2h
Si ha quindi
n n h n n i
2h ≥ n! > ⇒ log2 (2n ) > log2
e e
n
h > n · log2 = n · log2 n − n · log2 e
e
Poichè n · log2 n − n · log2 e ∼ n · log2 n per n → +∞ allora h > n · log2 n. Si ha quindi che la complessità
h sarà sempre maggiore a n · log n, quindi è limitata inferiormente
Si tralascia la base del logaritmo, in quanto il cambio di base necessita solo di una moltiplicazione per
una costante e quindi ininfluente al caso asintotico.
8
1.3.3 Bubble Sort
É un ordinamento che privilegia la posizionazione corretta veloce dei valori alti.
Come struttura dati di ingresso necessita un vettore.
Tale algoritmo ha un approccio incrementale, pertanto il vettore concettualmente sarà suddiviso in due
sottovettori:
• sottovettore sinistro, che contiene gli elementi ancora da ordinare, inizialmente coincide con il
vettore in ingresso
Si ha la terminazione dell’algoritmo nel caso in cui il sottovettore destro coincide con il vettore di ingresso,
dato ciò che è stato fin’ora esplicitato sarebbe necessario effettuare confronti per n − 1 elementi, dove
n è la dimensione del vettore, è possible anche cercare di ottimizzarlo facendo evitare dei controlli se
sono certamente inutili, una buona ottimizzazione si può avere aggiungendo un flag che mi indica se ci
sono stati scambi; se alla fine dell’iterazione non sono avvenuti scambi allora significa che il vettore è già
ordinato e quindi si può terminare precocemente l’ordinamento.
1 void bubble_sort ( int A [] , int n )
{
3 int i , j , temp ;
for ( i =0; i <n -1; i ++)
5 {
for ( j =0; j <n -1 - i ; j ++)
7 {
if ( A [ j ] > A [ j +1])
9 {
temp = A [ j ];
11 A [ j ] = A [ j +1];
A [ j +1] = temp ;
13 }
}
15 }
}
Listing 1.4: BubbleSort
void opt_bubble_sort ( int A [] , int n )
2 {
La versione ottimizzata potra dei miglioramenti nel caso medio, ma la complessità asintotica di caso
peggiore resta invariata.
9
Analisi asintotica L’algoritmo è composto da confronti, di costo unitario, e da due cicli:
• ciclo esterno eseguito (n − 1) volte
• ciclo interno viene eseguito, all’i-esima iterazione, (n − 1 − i) volte
Pertanto
n2
T (n) = (n − 1) + (n − 2) + . . . + 2 + 1 = = O(n2 )
2
10
/**
2 * A è il vettore da ordinare , n è la sua dimensione
* */
4 int min , max , i ;
min = max = A [0];
6 for ( i =1; i < n ; i ++)
{
8 if ( A [ i ] < min )
min = A [ i ];
10 else if ( A [ i ] > max )
max = A [ i ];
12 }
Ora è necessario realizzare il vettore delle occorrenze semplici (un vettore che nell’indice contiene il valore
della chiave e come contenuto contiene il numero delle sue occorrenze). Sarà necessario inizializzarlo
inizialmente a 0.
/**
2 * il vettore occ sarà allocato in qualche modo e avrà dimensione pari
* a max - min +1
4 * */
int i ;
6 for ( i =0; i <= max - min ; i ++)
occ [ i ]=0;
8 for ( i =0; i < n ; i ++)
int i ;
5 for ( i =n -1; i >=0; i - -)
11
14 out = ( int *) malloc ( sizeof ( int ) * n ) ;
for ( i =0; i < n ; i ++)
16 occ [ A [ i ] - min ]++;
for ( i =1; i < n ; i ++)
18 occ [ i ]+= occ [i -1];
for ( i =n -1; i >=0; i - -)
20 out [( occ [ A [ i ]] - -) - min ]= A [ i ];
return out ;
22 }
Listing 1.7: CountingSort
1 costante
log n logaritmico
n lineare
n · log n linearitmico
n2 quadratico
n3 cubico
2n esponenziale
Esempio 1 (Analisi asintotica nella ricerca lineare) Poichè in una ricerca il caso peggiore si ha
quando non viene trovata la chiave. Poichè abbiamo esaminato precedentemente tale tipo di ricerca e
si è osservato che si effettuano al massimo n passi, pertanto l’algoritmo è cresce linearmente con la
dimensione dei dati.
• ...
n
• Alla i-esima iterazione il vettore si riduce a contenere circa 2i elementi
1 ci si interessa principalmente del consumo di tempo, in quanto la memoria la si può immaginare pressocche infinita
12
L’algoritmo termina nel caso peggiore se la dimensione del vettore è di 1 solo elemento.
n
= 1 ⇒ n = 2i ⇒ i = log2 n
2i
L’algoritmo ha complessità logaritmica.
Esempio 3 (Analisi asintotica dell’ InsersionSort) Il ciclo esterno viene eseguito n − 1 volte. Nel
caso peggiore il ciclo interno scandisce tutto il vettore ordinato (il ciclo è eseguito al massimo n − 1 volte).
Pertanto
n
X n · (n − 1)
T (n) = 1 + 2 + 3 + . . . = i=
i=1
2
Definizione 1 (Notazione asintotica O) Si dice che T (n) appartiene a O(g) si a partire da un certo
valore n0 in poi g > T (n).
Definizione 2 (Notazione asintotica Ω) Tale notazione implica che esiste un limite inferiore alla
complessita asintotica di caso peggiore, ciò consente di dimostrare se un algoritmo è il migliore o meno.
Se si riesce a dimostrare che un algoritmo ha limite inferiore significa che non può esistere un altro
algoritmo con complessità migliore.
Definizione 3 (Notazione asintotica Ω) Si usa tale notazione implica che esiste una funzione che
schiaccia sia sopra che sotto, ovviamente con due coefficienti numerici, e ci da informazioni molto più
accurate sull’andamento esatte di T (n).
• reti di calcolatori (si può ottimizzare la rete riducendo il numero di connessioni ridondanti)
Immaginiamo di avere una rete con 10 nodi numerati da 0 a 9, con degli archi che rappresentano le
connessioni tra due nodi.
Implicitamente il verso non si esprime, realizzando cosı̀ un arco bidirezionale.
Si cerca di evitare l’uso di tutte le connessioni non necessarie (cioè già note in precedenza).
L’ipotesi fondamentale è che non esiste una struttura dati esistente (e completa a priori), ma di volta in
volta che si aggiunge un collegamento si aggiorna la struttura (l’OnlineConnectivity realizza la struttura
al volo).
Bisognerà istituire due operazioni astrette:
13
Se voglio inserire (p, q) devo verificare che f ind(p) 6= f ind(q) in tal condizione bisogna unire gli insiemi
utilizzando union(f ind(p), f ind(q)).
Per implementare tale struttura dati occorre una struttura dati di vettore (struttura presente in tutti i
linguaggi di programmazione).
Occorre decidere se avere:
• union: scansione del vettore per cambiare gli ellementi che valgono p in q, O(n)
int i , t , p , q , id [ N ];
6 for ( i =0; i < N ; i ++)
id [ i ] = i ;
8 printf ( " Input pair p q : " ) ;
while ( scanf ( " % d % d " , &p , & q ) ==2)
10 {
if ( id [ p ] == id [ q ])
12 printf ( " pair % d % d already connected \ n " , p , q ) ;
else
14 {
for ( t = id [ p ] , i = 0; i < N ; i ++)
16 if ( id [ i ] == t )
id [ i ] = id [ q ];
18 printf ( " pair % d % d not yet connected \ n " , p , q ) ;
}
20 printf ( " Input pair p q : " ) ;
}
22 system ( " pause " ) ;
}
Listing 1.8: Quick Find
14
Si forma una catena di riferimenti (tale catena termina quando id[i]=i.
leggi la coppia (p, q)
calcola la testa della catena di p e di q, cioè (id[p])∗ e (id[q])∗. se (id[p])∗ è diverso da (id[q])∗ allora
aggiungi il nuovo riferimento id[(id[p])*] = (id[p])*
• find: percorre una catena che al massimo può essere lunga quanto il vettore, O(n)
int i , j , p , q , id [ N ];
6 for ( i =0; i < N ; i ++)
id [ i ] = i ;
8 printf ( " Input pair p q : " ) ;
while ( scanf ( " % d % d " , &p , & q ) ==2)
10 {
for ( i = p ; i != id [ i ]; i = id [ i ]) ;
12 for ( j = q ; j != id [ j ]; j = id [ j ]) ;
if ( i == j )
14 printf ( " pair % d % d already connected \ n " , p , q ) ;
else
16 {
id [ i ] = j ;
18 printf ( " pair % d % d not yet connected \ n " , p , q ) ;
}
20 printf ( " Input pair p q : " ) ;
}
22 system ( " pause " ) ;
}
Listing 1.9: Quick Union
1.6.1 Grafo
Informalmente un grafico è stato usato nella trattazione della Online Connectivity.
Il grafo è utilizzato in una grandissima quantità di algoritmi ed è una struttura dati fondamentale per
l’informatica.
• l’insieme degli archi o entità che introducono una relazione binaria tra due vertici, E
G = (V, E)
Definizione 5 (Grafo non orientato) Si definisce grafo non orientato un grafo nel quale se (u, v) ∈ E
allora esiste una relazione tra u e v e anche tra v e u
15
A B C
D E F
Figura 1.1: Esempio di grafo non orientato
Esempio 4
G = (v, e)
v = {A, B, C, D, E, F }
Definizione 6 (Grafo orientato) Si definisce grafo orientato un grafo nel quale se (u, v) ∈ E allora
esiste una relazione tra u e v allora non è detto che esiste una relazione tra v e u
A B C
D E F
Figura 1.2: Esempio di grafo orientato
Esempio 5
G = (v, e)
v = {A, B, C, D, E, F }
e = {(A, B), (B, B), (B, D), (B, E), (D, A), (D, E), (E, D), (F, C)}
a adiacente b ⇔ (a, b) ∈ E
16
1.6.3 Grado di un vertice
Definizione 7 (Grado di un vertice) Si definisce grado di un vertice il numero di entità incidenti del
nodo.
degree(A)
Nel caso in cui si ha un grafo orientato occorre distinguere il numero di entità entranti e uscenti dal nodo.
Si definisce quindi
Cammino p : u →p u0 G = (V, E)
cioè
∃(v0 , . . . , vk ) : u = v0 , u0 = vk , ∀i ∈ [1, k] ∩ N, (vi−1 , vi ) ∈ E
Si definisce k la lunghezza del cammino, numero di archi che compongono il cammino.
Si dice che u0 è raggiungibile da u se ∃p : u →p u0 .
Un cammino si dice semplice se ogni nodo, del percorso p, è visitato solo una volta.
Vi sono dei cammini particolari, tali cammini sono i cicli e hanno la caratteristica di iniziare e finire nello
stesso nodo (il ciclo più corto si ha in presenza di un cappio).
Un grafico che non presenta dei cicli si dice aciclico.
Esempio 6 d è raggiungibile da a?
G = (V, E)
V = {a, b, c, d}
E = {(a, b), (b, b), (b, c), (b, d), (c, d), (d, c), (d, a)}
p : a →p d : (a, b), (b, c), (c, d)
Si osserva che p è semplice ed è di lunghezza 3.
É importante specificare che sia il sottografo massimale, cioè non esiste un sottografo connesso che
contenga la componente connessa.
I vertici appartenenti alla componente connessi sono tutti mutuamente raggiungibili.
Esempio 7
G = (V, E)
V = {a, b, c, d}
E = {(a, b), (a, c), (a, d), (b, c), (b, d), (c, d)}
{a, c} è un sottografo connesso. {a, b, c} è un sottografo connesso, ma {a, b} ⊂ {a, b, c}. {a, b, c, d} è un
sottografo connesso, ma {a, b, c} ⊂ {a, b, c, d}. Poichè {a, b, c, d} = V allora {a, b, c, d} è la componente
connessa.
17
1.6.6 Connessione nei grafi orientati
Definizione 10 (Grafo fortemente connesso) Si definisce grafo fortemente connesso un grafo nel
quale esiste un percorso tra due nodi qualsiasi dello stesso, in entrambi i sensi di percorrenza.
Esempio 8
G = (V, E)
V = {a, b, c, d, e, f, g, h}
E = {(a, b), (b, c), (b, e), (b, f ), (c, d), (c, g), (d, c), (d, h), (e, a), (e, f ), (f, g), (g, f ), (g, h)}
In G sono presenti quattro componenti connesse:
• C1 = {a, b, e}
• C2 = {c, d}
• C3 = {f, g}
• C4 = {h}
Se un vertice, nel caso specifico h, non è mutuamente connesso a nessun altro vertice allora esso
rappresenta un’unica componente fortemente connsessa.
Definizione 12 (Grafo denso) Un grafo si dice denso se la cardinalità di E è circa uguale al quadrato
della cardinalità di V .
|E| ' |V |2
Definizione 13 (Grafo sparso) Un grafo si dice sparso se la cardinalità di E è circa uguale al quadrato
della cardinalità di V .
|E| |V |2
Definizione 14 (Grafo completo) Un grafo si dice completo ogni vertice vertice è collegato con tutti
gli altri nodi.
Un grafo completo è il grafo più denso che si può realizzare.
Un grafo completo ha
|V | |V |! |V | · (|V | − 1)
|E| = = = nel caso di grafo non orientato
2 2! · (|V | − 2)! 2
|V | |V |!
|E| = 2 · =2· = |V | · (|V | − 1) nel caso di grafo orientato
2 2! · (|V | − 2)!
Definizione 15 (Grafo pesato) Si definisce grafo pesato un grafo nel quale esiste una funzione peso
Generalmente il peso è un numero che è relazionato al tipo di informazioni che deve dare tale valore.
2 la cardinalità indica il numero di elementi contenuti dall’insieme, la cardinalità di A si indica con |A|
18
1.6.9 Alberi
Definizione 16 (Albero non radicato) Si definisce albero non radicato un grafo non orientato, con-
nesso e aciclico.
Definizione 17 (Albero, foreste) Si definisce foresta un grafo non orientato e aciclico.
Proprietà
G = (V, E) |E| numero degli archi |V | numero dei nodi
Se G è un albero non radicato allora
• tutte le coppie dei nodi sono connesse da un cammino semplice e unico
• rimuovendo un qualsiasi arco di G comporta la disconnessione del grafo
• si ha |E| = |V | − 1, poichè G è connesso ed è aciclico
• aggiungendo un nuovo arco tra gli archi presenti allora ottiene un ciclo
• esiste r ∈ E detto radice
• relazione di parentela
– y è un antenato di x se y ∈ p con p : x →p r
– y è un antenato proprio di x, se y è un antenato di x e x 6= y
– x è padre/figlio di y se x e y sono adiacenti
– r non ha padri
– x (foglia) non ha figli
Definizione 18 (Foglia) Si definisce foglia un nodo che non ha nodi adiacenti (nodi diversi dal nodo
padre).
Definizione 19 (Grado di un albero) Si definisce grado di un albero il numero minimo dei figli am-
messi da un qualsiasi nodo dell’albero.
Definizione 20 (Profondità di un nodo) Si definisce profondità di un nodo la lunghezza del cammino
che congiunge il nodo alla radice dell’albero.
Definizione 21 (Altezza di un albero) Si definisce altezza di un albero la lunghezza massima del
cammino che congiunge un nodo alla radice.
Altezza(A) = max profondità(E)
x∈E
Casi particolari
Una classe particolare di alberi sono gli alberi binari, cioè gli alberi con grado due.
Definizione 22 (Albero binario) Si definisce albero binario, con definizione ricorsiva, un albero con
grado due composto da:
• radice
• sottoalbero sinistro (albero binario)
• sottoalbero destro (albero binario)
Definizione 23 (Albero binario completo) Si definisce albero binario completo un albero binario in
cui tutti i percorsi radice-foglia hanno la stessa lunghezza, h e ogni nodo (eccetto le foglie ha grado due).
Detta h l’altezza dell’albero allora
• l’albero ha 2h foglie
h
X
• l’albero ha 2i = 2h+1 − 1 nodi
i=0
Definizione 24 (Albero bilanciato) Si definisce albero bilanciato un albero in cui tutti i cammini
radice foglia hanno la stessa lunghezza.
Se un albero è completo allora è bilanciato.
Definizione 25 (Albero quasi bilanciato) Si definisce albero quasi bilanciato un albero in cui tutti i
cammini radice foglia hanno circa la stessa lunghezza, può differire di al massimo 1.
19
1.7 La ricorsione
Definizione 26 (Procedura ricorsiva) Si definisce ricorsiva una procedura che è definita mediante la
chiamata diretta o indiretta (cioè con la chiamata ad un altra procedura che richiama la prima procedura)
alla procedura stessa.
Con la ricorsione si ha la partizione di un problema grande in un numero finito di problemi della stessa
natura3 , ma più semplici (si riduce la dimensione del problema).
La ricorsione consente di avere soluzioni più compatte ed eleganti.
Nel caso di procedure ricorsive è sempre necessario, per garantirsi la terminazione della stessa, una
condizione di terminazione cioè una soluzione ad un problema banale (che non può essere ulteriormente
separato).
Esempio 9 (Fattoriale)
n · (n − 1)! n≥1
n! =
1 n=0
1 long fattoriale ( int n )
{
3 if ( n ==0)
return 1;
5 else
return n * fattoriale (n -1) ;
7 }
Listing 1.10: Fattoriale ricorsivo
Esempio 12 (Valutazione di espressioni in forma prefissa ricorsva) Bisogna istituire una gram-
matica delle forme prefisse (notazione polacca). Una espressione in forma prefissa è definita nel seguente
modo:<op> <exp> <exp>
Si semplifica la forma prefissa utilizzando come operatori i soli operatori di somma e moltiplicazione.
<exp> = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<op> = + | *
Ma un’espressione può essere definita in modo ricorsivo <exp> = <exp> | <op> <exp> <exp>
Utilizzando la forma prefissa non sono più necessarie le parentesi
3 paradigma del Divide et Impera
20
Esempio: (5 + 12) ∗ 3 ≡ ∗ + 5 12 3
Esempio 13 (Ricerca binaria ricorsiva) Ha la stessa definizione della ricerca binaria, ma ne varia
la chiamata alla funzione, rendendola ricorsiva.
int dicotomica ( int vet [] , int l , int r , int k )
2 {
if (r < l )
4 return -1;
else if ( vet [( l + r ) /2]== el )
6 return ( l + r ) /2;
else if ( vet [( l + r ) /2] > el )
8 return dicotomica ( vet , l , ( l + r ) /2 -1 , k ) ;
else
10 return dicotomica ( vet , ( l + r ) /2+1 , r , k ) ;
}
Listing 1.14: Ricerca binaria ricorsiva
21
11 else
return v ;
13 }
Listing 1.15: Massimo di un vettore ricorsivo
22
Algoritmi ricorsivi di ordinamento
Merge Sort L’algoritmo prevede di dividere, rispetto il centro, il vettore in due sottovettori
si esegue il mergesorto sul sottovettore destro e sinistro (terminazione se p==r || p>r cioè è ordinato)
Ricombinazione, si fondono i due sottovettori ordinati in un vettore ordinato.
void mergeSort ( int A [] , int l , int r )
2 {
int q ;
4 if (l < r )
{
6 q = ( l + r ) /2;
mergeSort (A , l , q ) ;
8 mergeSort (A , q +1 , r ) ;
Merge (A , l , q , r ) ;
10 }
}
12 void Merge ( int A [] , int l , int q , int r )
{
14 int i , j , k , * B ;
B = ( int *) malloc ( r * sizeof ( int ) ) ;
16 if ( B == NULL )
return ;
18 for ( i =l , j = q +1 , k = l ; i <= q && j <= r ; )
{
20 if ( A [ i ] < A [ j ])
B [ k ++] = A [ i ++];
22 else
B [ k ++] = A [ j ++];
24 }
for ( ; i <= q ; )
26 B [ k ++] = A [ i ++];
for ( ; j <= r ; )
28 B [ k ++] = A [ j ++];
for ( k = l ; k <= r ; k ++)
30 A [ k ] = B [ k ];
}
Listing 1.16: Mergesort
Analisi asintotica di caso peggiore Si ipotizza per comodità n = 2k (lo si può porre se si considera
n ≥ 2k )
Dividi: calcola la metà di un vettore D(n) = Θ(1)
Risolvi: risolve i due sottoproblemi di dimensione n2 (a = b = 2)
Test di terminazione semplice Θ(1)
Ricombinazione: basata sul merge C(n) = Θ(n) La dimensione del problema è n = 2k (per comodità,
ma non è vincolante).
All’i-esimo passo, poichè si divide a metà la dimensione del problema, si avrà che la dimensione è 2ni .
La condizione di terminazione è che il problmema ha dimensione 1,
n
= 1 ⇒ i = log2 n
2i
23
Quick Sort É un algoritmo di ordinamento in loco, non stabile che risulta sperimentalmente più rapido
del mergesort.
In realtà il quick sort è un algoritmo di complessità quadratica nel caso peggiore, mentre nel caso medio
e migliore ha complessita linearitmica. Si utilizza tale algoritmo di ordinamento in quanto nel caso medio
è più veloce del mergesort, non richiede l’allocazione di un vettore aggiuntivo e il caso peggiore (vettore
ordinato in modo crescente o decrescente) è facilmente eliminabile.
la scelta del metodo di scelta del pivot è arbitraria. Si sceglierà comunque di avere una regola fissa per
la scelte del pivot, cioè si prende il primo elemento del vettore.
int q ;
4 if (l < r )
{
6 q = partition (A , l , r ) ;
quickSort (A , l , q ) ;
8 quickSort (A , q +1 , r ) ;
}
10 }
int x , i , j , temp ;
14 x = A [ l ];
i = l -1;
16 j = r +1;
while (i < j )
18 {
while ( A [ - - j ] >= x )
20 ;
while ( A [++ i ] < x )
22 ;
if (i < j )
24 {
temp = A [ i ];
26 A [ i ] = A [ j ];
A [ j ] = temp ;
28 }
}
30 return j ;
}
Listing 1.17: Quick Sort
4 si ha anbiguità in quanto è a discrezione del programmatore il posizionamento del pivot nel sottovettore destro o sinistri
24
Ricombinazione non presente, in quanto si ordina direttamente sul vettore di partenza.
Analisi asintotica di caso peggiore L’efficienza dell’algoritmo è dipendente dal bilanciamento delle
partizioni, più le partizioni sono bilanciate tanto più è efficiente.
Il caso peggiore si ha in caso di parizionamento completamente sbilanciato, cioè nel caso in cui si ha un
sottovettore di 1 elemento e un sottovettore di n − 1 elementi.
T (n) = O(n2 )
Si osserva che nel caso peggiore si ha una complessità quadratica, ma tale caso si può escludere con grande
semplicità.
Caso migliore si ha nel perfetto bilanciamento della partizione, si ha una situazione esattamente
equivalente al mergesort quindi
T (n) = O(n · log n)
Caso medio ci si trova nel caso medio praticamente sempre purchè non si sia nelle condizioni di
caso peggiore. In tale situazione l’algoritmo ha
Esempio 15 (Quick sort nel caso medio) Sipponiamo che a ogni partition si ha un sottovettore di
9 1
10 · n (sx) e un altro sottovettore di 10 · n (dx).
Discendendo l’albero della ricorsione si osserva che il percorso estremo sinistro è “lungo” log 10
9
(n), mentre
l’albero estremo destro è “lungo” log10 (n) e per ogni passo si eseguono n operazioni per la partition.
Poichè
log 10
9
(n) ≈ log10 (n) n → +∞
allora ha complessità T (n) = O(n · log n)
La scelta del pivot non cambia la complessità, ma può influenzare le costanti che sono presenti all’in-
terno del T (n) reale, tale scelte può essere ottimizzata in base ai campi applicativi, attraverso euristiche
particolari o semplici.
25
n
T (n) = 1 + 1 + T
8
Il caso terminale si ha quando n = 1, supponendo n una potenza di 2 (per comodità, ma questo non
cambia il risultato)
log2 n
X
T (n) = i = log2 n + 1
i=0
T (n) = O(log n)
Massimo in un vettore
n
Il problema è scomposto in due problemi di dimensione 2, non si ha ricombinazione.
n
2·T 2 +1 n>1
T (n) =
1 n=1
n n
T =2·T +1
2 4
n n
T =2·T +1
4 8
n n
T =1+2+2·T
2 8
Il caso terminale si ha quando n = 1, quindi
log2 n
X 2log2 n+1 − 1
T (n) = 2i = = 2log2 n+1 − 1 = 2 · n − 1
i=0
2−1
T (n) = O(n)
La ricerca del massimo effettuata in modo iterativo ha la stessa complessità dell’algoritmo analizzato, ma
effettuando una sola scansione allora effettua al massimo n passi, quindi poichè ha delle costanti minori
è migliore la ricerca del vettore in modo iterativo.
Risoluzione
• Si spostano n − 1 dischi sul piolo ausiliare, piolo che non è quello di partenza ne quello di arrivo.
26
Analisi di complessità
La divisione è un’operazione elementare D(n) = Θ(1)
La ricomposizione è un’operazione elementare C(n) = Θ(1)
La risoluzione è:
• se n > 1 divide il problema in due sottoproblemi di dimensione n − 1
• se n = 1 si ha la condizione di terminazione
2 · T (n − 1) + 1 n>1
T (n) =
1 n=1
T (n − 1) = 1 + 2 · T (n − 2)
T (n − 2) = 1 + 2 · T (n − 3)
n+1
X
T (n) = 1 + 2 + 4 + 8 · T (n − 3) = 2i = 2n − 1
i=0
n
T (n) = O (2 )
Si osserva che questo gioco matematico è certamente decidibile ma con n sufficientemente grande il
problema è intrattabile.
1 void Hanoi ( int n , int src , int dest )
{
3 int aux ;
aux = 3 - ( src + dest ) ;
5 if ( n == 1)
{
7 printf ( " src % d -> dest % d \ n " , src , dest ) ;
return ;
9 }
Hanoi (n -1 , src , aux ) ;
11 printf ( " src % d -> dest % d \ n " , src , dest ) ;
Hanoi (n -1 , aux , dest ) ;
13 }
Listing 1.18: Torri di Hanoi
1.7.4 Backtracking
Attraverso il divide et impera si suppone di essere in grado di arrivare alla soluzione terminale.
Nel caso in cui questo non sia vero occorre essere in grado di percorrere esaustivamente lo spazio delle
soluzioni, tale procedura è nota come backtracking.
∀i 6= r key(parent(i)) ≥ key(i)
27
• Heapify(A, i), funzione che introduce i all’interno dell’heap
• Buildheap(A), funzione che trasforma una struttura dati, ad esempio un vettore, in un heap
• Heapsort(A), funzione di ordinamento ottimizzata per questa tipologia di dato, tale algoritmo sarà
un algoritmo ottimo [O(n · log(n))]
Una coda senza priorità è una struttura di tipo FIFO5 ; mentre una coda prioritaria, come la coda a un
pronto soccorso, è facilmente strutturabile attraverso l’uso di un heap, per tale coda è necessario definire
le seguenti funzioni
• insert(S, x)
• maximum(S), Θ(1)
• extract_maximum(S), O(log(n))
Un heap può essere implementato attraverso una struttura dinamica che ha puntatori all’elemento padre,
figlio destro e figlio sinistro; ma nell’uso pratico si preferisce adottare un vettore allocato dinamicamente;
ma par mantenere le relazioni padre, figli è necessario o avere tre vettori conteneti tutti gli indici oppure
si può avere un grande risparmio della memoria organizzando in un modo specifico la memoria.
Il figlio sinistro di i sarà identificato da LEFT(i) = 2 * i + 1,
il figlio destro di i sarà identificato da RIGHT(i)= 2 * i + 2
il padre di i sarà identificato da PARENT(i)= (i-1)/2
Tale operazioni sono di complessità costante, in quanto sono una somma o delle moltiplicazioni/divisioni
per due, semplici shift a sinistra o destra.
Per questa implementazione è necessario “sovradimensionare” il vetttore, si osserverà che allocando un
livello in eccesso si può sprecare al massimo metà del vettore. Per tale implementazione sarà anche
necessario fornire una funzione che restituisca il numero di elementi precedentemente inseriti nell’heap
heapsize(A).
28
void swap ( int A [] , int first , int second )
2 {
int t ;
4 t = A [ first ];
A [ first ] = A [ second ];
6 A [ second ] = t ;
}
8 void heapify ( int A [] , int i , int heapsize )
{
10 int l , r , largest ;
l = LEFT ( i ) ;
12 r = RIGHT ( i ) ;
if (l < heapsize && A [ l ] > A [ i ])
14 largest = l ;
else
16 largest = i ;
if (r < heapsize && A [ r ] > A [ largest ])
18 largest = r ;
if ( largest != i )
20 {
swap (A , i , largest ) ;
22 heapify (A , largest , heapsize ) ;
}
24 }
Listing 1.20: Procedura di heapify in un heap
Questa procedura ha T (n) = O(n), non lo si può dimostrare intuitivamente ma lo si può fare
analiticamente.
1.8.4 HeapSort
L’ordinamento di un heap è eseguito con i seguenti passi:
29
1.9 ADT: Tabella di simboli (Symbol Table)
Definizione 28 (Symbol table) Una tabella di simboli è una struttura di dati formata da record con
chiave. Su questa struttura possono essere definite le seguenti operazioni di base:
Tale ADT è detto anche dizionario se all’utente è consentito solo fare ricerche.
• Strutture lineari:
• Strutture ad albero:
30
Ricerca sequenziale
Tale tipo di ricerca è applicabile solo su un array o una lista concatenata.
Array ordinato :
inserimento con spostamento, di una posizione, degli elementi più grandi (rispetto alla chiave)
ricerca mediante la scansione sequenziale, si termina se la chiave è stata trovata o la chiave corrente
è superiore della chiave da cercare
inserimento in fondo
ricerca mediante la scansione sequenziale completa, si termina se la chiave è stata trovata è
terminata la struttura
Lista ordinata :
inserimento in testa
ricerca mediante la scansione sequenziale completa, si termina se la chiave è stata trovata è
terminata la struttura
Ricerca binaria
Mantenere l’ordinamento dell’arrai in fase di inserimento ha costo quadratico.
Tale ricerca è vantaggiosa in situazioni statiche:
L’efficienza è legata alla capacità di accesso diretto alle celle dell’array, pertanto è svantaggioso usare liste
concatenate.
Definizione 29 (BST) Si definisce BST (Binary Search Tree) un albero binario con la seguente pror-
pietà funzionale:
∀y ∈ lef t(x), key(y) ≤ key(x)
∀x ∈ BST :
∀z ∈ right(x), key(z) ≥ key(x)
31
1.10.1 Operazioni in un BST
Operazioni definite in questo tipo di struttura dati sono:
search Si effettua una ricerca ricorsiva di un nodo con chiave v.
Si percorre l’albero dalla radice, si termina se v è la chiave della radice dell’albero (search hit) oppure
se l’albero è vuoto (search miss); la ricorsione dal nodo x al sottoalbero sinistro se v < key(x) o al
sottoalbero destro se v > key(x).
minumum si segue fino al sottoalbero sinistro finchè esiste
maximum si segue fino al sottoalbero destro finchè esiste
predecessor il predecessore del nodo x è il nodo con la più grande chiave minore di x.
Se ∃lef t(x) predessor(x) = max(lef t(x)); mentre se 6 ∃lef t(x) predecessor(x) =primo antenato di
x il cui figlio destro è anche antenato di x
successor il successore del nodo x è il nodo con la più piccola chiave maggiore di x.
Se ∃right(x) successor(x) = min(right(x)); mentre se 6 ∃right(x) successor(x) =primo antenato di
x il cui figlio sinistro è anche antenato di x
insert l’insersione di un nodo z con chiave v implica anche il mantenimento delle proprietà funzionali.
Se il BST è vuoto si crea un nuovo BST con la chiave v posta nella radice, mentre se il BST non è
vuoto si opera ricorsivamente (inserendo nel sottoalbero sinistro/destro l’elemento) o iterativamente
(che ricerca la posizione in cui introdurre il nodo e dopo lo introduce).
sort attraversamento del BST in-order (per avere ordinamento crescente)
select selezione della k-esima chiave più piccola; per questa operazione sarebbe conveniente avere un
informazione aggiuntiva sull’albero che contiene il numero di nodi contenuti dal nodo (si potrebbe
anche linearizzare il BST ma si evita attraverso la supposizione precedente).
Se si ricerca la chiave minima si pone k = 0 (t è il numero di nodi radicati nel sottoalbero destro); se
t = k si ritorna la radice dell’albero, se t > k si ritorna la k-esima chiave più piccola del sottoalbero
sinitro, infine se t < k si ricerca la (k − t − 1)-esima chiave più piccolo del sottoalbero destro.
inserimento nella radice inserimento di una nuova chiave nella posizione corretta mediante la proce-
dura di insert e attraverso una serie di rotazioni dell’albero si fa risalire l’elemento lungo l’albero.
delete si elimina un elemento dal BST; se l’elemento è una foglia la si elimina semplicemente, mentre
se il nodo ha un solo sottoalbero si collega il padre con il figlio (si rimuove facilmente); mentre se
il nodo ha entrambi i due sottoalberi occorre prestare molta attenzione, cioè eliminata la radice
di un albero è necessario ricollegare i due sottoalberi (restati disgiunti) ponendo sulla radice il
predecessore o successore della radice (attraverso un’operazione di partition).
rotazione a sinistra Dato h il puntatore al nodo con chiave x, lef t(h) = α, right(h) = y, lef t(y) = β
e right(y) = γ si operano in modo elementare le seguenti operazioni
32
Partition
L’operazione consiste nel riorganizzare il BST ponendo nella radice la k-esima chiave più piccola nella
radice. Si può operare in modo ricorsivo, si pone inizialmente il nodo come radice di un albero: se
t (numero di nodi del sottoalbero sinistro) è maggiore di k si effettua la procedura di partition sul
sottoalbero sinistro con chiave k; mentre se t < k si effettua la procedura di partition sul sottoalbero
destra con chiave k = k − t − 1, al termine della ricorsione si ruota l’albero nella direzione opposta alla
direzione presa per la partition.
1.10.4 Implementazione
;;;;;;
33
34
Parte II
Programmazione
35
Capitolo 2
37
2.1.2 Strategie di soluzione
La maggioranza dei problemi risolti mediante programmi consiste nell’elaborazione di informazioni rice-
vute in input, per produrre risultati di output.
Per la stesura dell’algoritmo consiste nel:
• Individuazione dei dati (dei vari passaggi: input, output e dati intermedi)
• Formalizzazione delle operazioni necessarie per ottenere i risultati intermedi e quelli di output
• Cercare nella propria esperienza personale passaggi noti (un algoritmo è un progetto)
• Cercare metodi risolutivi (magari lavorando su carta) per risolvere il problema, nel caso di assenza
di altri strumenti
Per la scelta delle strutture dati oggorre ragionare su tutte le variabili necessarie durante lo svolgimento
di tutti i passaggi.
• dati numerici
Hanno il vantaggio di essere spesso già formalizzati in modo non ambiguo, mentre hanno lo svantaggio
che i numeri, all’interno del calcolatore, hanno lunghezza finita e spesso sono arrotondati.
Problemi numerici non iterativi Problemi nei quali occorre semplicemente effettuare una serie di
scelte.
Problemi numerici iterativi Come problemi su successioni/serie o poligoni di n lati. [Problema della
serie armonica] Si richiede di introdurre n. Se n ≤ 0 non si esegue nulla, altrimenti si calcola la ridotta
n-esima
n
X 1
i=1
i
# include < math .h >
2 # include < stdio .h >
in main ( void )
4 {
int n , i ;
6 float M ;
printf ( " Introduci il numero di termini (0 = fine ) \ n " ) ;
8 scanf ( " % d " , & n ) ;
while ( n > 0)
10 {
M = 0.0;
12 for ( i = 1; i <= n ; i ++)
M = M + 1.0/(( float ) i ) ;
14 printf ( " Risultato : % f " , M ) ;
printf ( " Introduci il numero di termini (0 = fine ) \ n " ) ;
16 scanf ( " % d " , & n ) ;
}
18 return 0;
}
Listing 2.1: Calcolo della ridotta n-esima di una serie armonica
38
Problemi di codifica/decodifica Problemi nel quale bisogna occuparsi direttamente della codifica:
• numerica (conversione tra basi, operazioni in determinate basi)
• non numerica (decodifica/riconoscimento di codici interni)
int p ;
4 for ( p = 1; 2* p <= n ; p = p *2) ;
while ( p > 0)
6 {
if ( p <= n )
8 {
printf ( " 1 " ) ;
10 n = n-p;
}
12 else
printf ( " 0 " ) ;
14 p = p /2;
}
16 printf ( " \ n " ) ;
}
Listing 2.2: Visualizzazione della codifica binaria di un intero
Si intende realizzare un programma che acquisisca iterativamente numeri interi dalla base iniziale b0 e
li converta e stampi in base b1 . Bisogna terminare l’esecuzione in presenza di inserimento di cifre non
valide.
1 # include < stdio .h >
void converti_base ( int n , int base ) ;
3 int main ( void )
{
5 int b0 , b1 , n = 0 , cifra , fine = 0;
char c ;
7 // menù per l ’ inserimento di b0 , b1
while (! fine )
9 {
scanf ( " % c " , & c ) ;
11 if ( c == ’ ’ || c == ’\ n ’)
{
13 converti_base (n , b1 ) ;
n = 0;
15 }
else
17 {
cifra = c - ’0 ’;
19 if ( cifra >= 0 && cifra <= b0 )
b = b0 * n + cifra ;
21 else
39
fine = 1
23 }
}
25 return 0;
}
27 void converti_base ( int n , int base )
{
29 int i , p ;
for ( p = 1; p * base <= n ; p = p * base ) ;
31 while ( p > 0)
{
33 if ( p <= n )
{
35 printf ( " % d " , n / p ) ;
n = n%p;
37 }
else
39 printf ( " 0 " ) ;
p = p / base ;
41 }
printf ( " \ n " ) ;
43 }
Listing 2.3: Conversione da base b0 a base b1 (iterativo)
• ogni codice alfabetico minuscolo viene scambiato in maiuscolo e viene fatto il complemento a 0 z 0 .
ch ←0 A0 + (0 z 0 − ch)
• ogni codice alfabetico maiuscolo viene scambiato in minuscolo e viene fatto il complemento a 0 Z 0 .
ch ←0 a0 + (0 Z 0 − ch)
Algoritmo
40
printf ( " File % s non aperto .\ n " , nomefile ) ;
21 return 1;
}
23 while (! feof ( fin ) )
{
25 fscanf ( fin , " % c " , & ch ) ;
if ( ch >= ’0 ’ && ch <= ’9 ’)
27 ch = ’0 ’ + ( ’9 ’ - ch ) ;
else if ( ch >= ’a ’ && ch <= ’z ’)
29 ch = ’Z ’ + ( ’z ’ - ch ) ;
else if ( ch >= ’A ’ && ch <= ’Z ’)
31 ch = ’z ’ + ( ’Z ’ - ch ) ;
fprintf ( fout , " % c " , ch ) ;
33 }
fclose ( fin ) ;
35 fflush ( fout ) ;
fclise ( fout ) ;
37 return 0;
}
Listing 2.4: Codifica di un file
Elaborazione testi carattere per carattere Sono problemi che fanno rferimento a problemi di
elaborazione testi (chiaramente non si possono usare font, colori, dimenzioni, . . . ). Sarebbe utile elaborare
carattere per carattere quando non ci sono già implementate nel linguaggio (ossia per casi particolari,
facendo attenzione ad aggiungere sempre il NULL-terminator 1 .
di caratteri
41
13 printf ( " Intervallo per ordinate : " ) ;
scanf ( " % f % f " ,& ymin ,& ymax ) ;
15 passo = ( xn - x0 ) / n ;
for ( i =0; i <= n ; i ++)
17 {
x = x0 + i * passo ;
19 y = a*x*x + b*x + c;
if (y < ymin || y > ymax ) continue ;
21 for ( j = round (y - ymin ) ; j >0; j - -)
printf ( " " ) ;
23 printf ( " *\ n " ) ;
}
25 }
Listing 2.5: Grafico di una parabola su una interfaccia a carattere
42
33 scanf ( " % s " , nomein ) ;
printf ( " nome file in uscita : " ) ;
35 scanf ( " % s " , nomeout ) ;
printf ( " massima lunghezza riga : " ) ;
37 scanf ( " % d " , & l ) ;
formatta ( nomein , nomeout , l ) ;
39 }
Listing 2.6: Riformattazione testi − mediante sottostringhe
Problemi di verifica e di filtri Sono problemi in cui bisogna decidere se un insieme di dati/infor-
mazioni rispettano un determinato criterio di accettazione (si ha sempre una risposta SI/NO). Si possono
verificare dati singoli o più dati insieme.
Un problema di selezione è del tipo: prima verifico e poi decido.
Verificare una sequenza di dati significa decidere se la sequenza rispetta un determinato criterio di acce-
tazione, come introdurre parole in ordine alfabetico o se voglio tutte parole senza consonanti o problemi
simili.
Dato un file di testo contenente cognome e nome (su una riga di massimo 50 caratteri), si scriva una
funzione che ritorini 1 se è in ordine alfabetico altrimenti ritorini 0.
int verifica_ordine ( FILE * fp )
2 {
43
44
Capitolo 3
I puntatori sono dei tipi di dati che contengono l’indirizzo di una locazione di memoria. Sui puntatori
sono variabili su cui comunque è possibile effettuare delle operazioni (algebra dei puntatori) e consentono
l’allocazione dinamica della memoria (nel C i vettori hanno una dimensione fissa e decisa in fase di
compilazione).
45
Assegnazione a variabili puntatori
p = &x oppure s=p (se p è un puntatore)
Puntatore generico
Esiste in C anche la possibilità di definire un puntatore di tipo generico, void *, tale puntatore può,
rispettando tutte le restrizioni del C (poichè è fortemente tipizzato) essere convertito da puntatore di int
a puntatore di float, . . . .
Un puntatore di questo tipo può esere comodo per puntatori di cui non ci interessa conoscere il tipo o
per puntatori addetti al solo trasporto di un indirizzo.
...
2 int * px ;
char * s0 ;
4 void * generic ;
...
6 generic = px ;
...
8 s0 = generic ;
...
Listing 3.1: Esempio di uso di void*
Ovviamente se il primo confronto è verificato allora lo è anche il secondo, la cosa non vale al contrario.
L’aritmetica dei puntatori è consigliabile non effettuarla su void* in quando non essendo il tipo void* un
tipo puntatore che non contiene la dimensione di ogni singolo elemento puntato potrebbe causare errori
inattesi.
46
int strlen ( char s [])
2 /** senza uso di puntatori , si usa solo un contatore */
{
4 int l =0;
while ( s [ l ]!= ’ \0 ’)
6 l ++;
return +;
8 }
Listing 3.3: strlen - I
int strlen ( char s [])
2 /** con puntatore e contatore */
{
4 int l =0;
char * p = & s [0]; // equivalente p = s ;
6 while (* p != ’ \0 ’)
{
8 p ++;
l ++;
10 }
return l ;
12 }
Listing 3.4: strlen - II
int strlen ( char s [])
2 /** con due puntatori e senza contatore */
{
4 char * p0 = s ;
char * p1 = s ;
6 while (* p1 != ’ \0 ’)
p1 ++;
8 return ( p1 - p0 ) ;
}
Listing 3.5: strlen - III
1 int strlen ( char s [])
/** un puntatore e senza contatore */
3 {
char * p = s ;
5 while (* p != ’ \0 ’)
p ++;
7 return (p - s ) ;
}
Listing 3.6: strlen - IV
47
3.4.1 Vettore come parametro di una funzione
In un paramentro formale un vettore può corrispondere a un puntatore come parametro attuale.
void ordinaInt ( int v [] , int n ) ;
2 int main ( void )
{
4 int dati [100];
...
6 for ( i =0; i <100; i +=10;
ordinaInt (& dati [ i ] , 10) ;
8 ...
}
Listing 3.7: Vettore come parametro di una funzione - I
Ma la relazione vale anche al contrario, se come parametro formale c’è un puntatore posso passare come
parametro attuale un vettore.
1 void leggiInt ( int *p , int n ) ;
int main ( void )
3 {
Si vuol realizzare una funzione che confronta due stringhe e che ritorni:
• 0 se le due stringhe sono uguali
• ¿0 se la prima stringa segue la seconda
• ¡0 se la prima stringa precede la seconda
É una funzione che ha lo stesso funzionamento della strcmp della libreria standard.
int strcmp ( char * s0 , char * s1 )
2 /** senza l ’ uso di alcuna variabile locale */
{
4 while ((* s0 ==* s1 ) && (* s0 != ’ \0 ’) )
{
6 s0 ++;
s1 ++;
8 }
return (* s0 -* s1 ) ;
10 }
Listing 3.9: strcmp - I
int strcmp ( char * s0 , char * s1 )
2 /** senza l ’ uso di alcuna variabile locale */
{
4 while ((* s0 ==* s1 ) && (* s0 != ’ \0 ’) )
{
6 s0 ++;
s1 ++;
8 }
return (* s0 -* s1 ) ;
10 }
Listing 3.10: strcmp - II
48
int strcmp ( char * s0 , char * s1 , int n )
2 /** strcmp solo su n caratteri */
{
4 int i =0;
while ( s0 [ i ]== s1 [ i ] && s0 [ i ]!= ’ \0 ’)
6 {
if (i < n )
8 i ++;
else
10 return 0;
}
12 return ( s0 [ i ] - s1 [ i ]) ;
}
Listing 3.11: strncmp
Si realizzi una funzione che ricerci una stringa all’interno di un’altra stringa (reimplementazione di
strstr della libreria). La funzione ritorna NULL se la sottostringa non è stata trovata, il puntatore
all’inizio della sottostringa se è stata trovata.
char * strstr ( char s [] , char cerca [])
2 {
...
8 struct sudente * p ;
float * p_media ;
10 ...
struct studente
49
6 {
char cognome [ MAX ] , nome [ MAX ];
8 int matricola ;
struct esame * es ;
10 };
...
12 p = ...; /** in qualche modo metto in p l ’ indirizzo di una struct
studente */
Se io ora volesi porre il voto dell’esame scritto dello studente puntato da p dovrei fare: (*(*p).es).
scritto=voto, ma come credo sia evidente richiede una grande attenzione alla precedenza degli operatori
(occorre aggiungere in posti esatti asterischi e parentesi). Il C ha introdotto una nuova simbologia per
indicare i campi di una struttura puntata
(*(* p ) . es ) . scritto = p - > es - > scritto ;
è chiaro che le due scritture sono equivalenti, ma con la nuova simbologia risulta di molta più semplice
comprensione.
int num ;
6 struct oggetto * succ ;
};
8 int main ( void )
{
10 int i , N , M ;
struct oggetto *p , oggetti [ MAX ];
12 printf ( " N = " \ n " ) ;
scanf ( " % d " , & N ) ;
14 printf ( " N = " \ n " ) ;
scanf ( " % d " , & M ) ;
16 for (
}
Listing 3.14: Esempio di Giuseppe Flavio
50
3.6 Allocazione dinamica della memoria
L’obbiettivo fondamentele nell’allocazione dinamica è avere un contatto diretto con l’allocazione di por-
zioni di memoria, capire che la memoria allocata è sotto lo stretto controllo del programmatore (che dovrà
capire dove, quanto e come allocare la memoria).
Allocare una variabile significa associare alla variabile una porzione di memoria (in cui collocare i dati).
L’allocazione avviene in modo:
• permanente, per le variabili globali (definite fuori da ogni funzione)
• temporaneo, per le variabili locali e i parametri formali
• dinamico, per le variabili allocate dinamicamente dal programmatore
Il C fornice metodi per l’allocazione e deallocazione esplicita basata su puntatori.
Allocare la memoria significa richiedere al Sistema operativo un determinato quantitativo di memoria e
ottenerla (se c’è abbastanza memoria).
Deallocare la memoria significa restituire (rilascare) la memoria precedentemente ottenuta, al Sistema
Operativa, in modo che il SO la possi riusare. In fase di deallocazione è solo necessario passare al sistema
operativo il punto in cui inizia la memoria allocata dinamicamente.
3.6.1 Malloc
La funzione malloc nel C ritorna un puntatore di tipo void * che punta a una memoria contigua di n
byte, nel caso non sia disponibile tutta la memoria richiesta ritorna un puntatore NULL. La funzione ha
il seguente prototipo
void * malloc ( size_t size ) ;
Listing 3.15: Prototipo malloc
size indica il numero di byte da richiedere al sistema operativo. É conveniente chiedere in fase di richiesta
di memoria, fare un cast esplicito al tipo a cui viene effettuata l’allocazione.
Per evitare di dover fare conti manualmente, spesso errati, sulla quantità di memoria da richiedere si
esegue una stategia molto semplicativa: si indica con n il numero di celle di memoria da allocare e con
sizeof(type) richiama la funzione sizeof che vuole come parametro il tipo della variabile e ne ritorna
la memoria occupata da ciascuna allocazione.
51
Cancellazione di un nodo (elemento)
Dato che si trattano specialmente liste linkate semplici, è necessario fermarsi all’elemento precedente del
quale bisogna cancellare. Denominando con x l’elemento precedente.
L’eliminazione è strutturata sostanzialmente da
In realtà una lista può anche essere implementata attraverso un vettore statico, in questo caso l’inseri-
mento, la cancellazione e l’estrazione di elementi della lista hanno complessità di O(n) in quanto occorre
shiftare tutti gli elementi per “ricompattare” il vettore, mentre l’accesso a un singolo elemento può essere
determinato dall’indice (complessità costante O(1)).
52
Parte III
Laboratorio
53
Capitolo 4
Esercitazione 1
55
system ( " pause " ) ;
24 return 1;
}
26 system ( " pause " ) ;
return 0;
28 }
void manipolazione ( float matrix [][ MAXDIM ] , int dim )
30 {
int i , j ;
32 for ( i =0; i < dim ; i ++)
{
34 for ( j =0; j < dim ; j ++)
{
36 if ( matrix [ i ][ j ] <=0)
matrix [ i ][ j ]=0;
38 else
matrix [ i ][ j ] = potenza_sup (10 , matrix [ i ][ j ]) ;
40 }
}
42 }
int potenza_sup ( int base , int numero )
44 {
int i =1;
46 while (i < numero )
i *= base ;
48 return i ;
}
50 int leggi_file ( char * nome_file , float matrix [][ MAXDIM ])
{
52 FILE * f ;
int i , j , dim ;
54 if (( f = fopen ( nome_file , " r " ) ) == NULL )
return 0;
56 fscanf (f , " % d " , & dim ) ;
for ( i =0; i < dim ; i ++)
58 {
for ( j =0; j < dim ; j ++)
60 {
if ( fscanf (f , " % f " , & matrix [ i ][ j ]) == EOF )
62 {
fclose ( f ) ;
64 return -1;
}
66 }
}
68 fclose ( f ) ;
return dim ;
70 }
int isSimmetrica ( float matrix [][ MAXDIM ] , int dim )
72 {
int i , j ;
74 for ( i =1; i < dim ; i ++)
{
76 for ( j =0; j < i ; j ++)
{
78 if ( matrix [ i ][ j ]!= matrix [ j ][ i ])
return 0;
80 }
}
82 return 1;
}
56
Listing 4.1: Manipolazione di una matrice - I
• la prima riga del file specifica le dimensioni reali della matrice (numero di righe nr e numero di
colonne nc). Si assuma che entrambi i valori siano comunque al più pari a 20.
• ciascuna delle nr righe successive contiene gli nc valori corrispondenti a una riga della matrice,
separati da uno o più spazi.
• generi una nuova matrice, in cui il valore di ciascun elemento è dato dalla media aritmetica delle
(al più) 8 caselle adiacenti all’elemento corrispondente della matrice di ingresso.
• scriva la matrice cosı̀ ottenuta su un file di uscita, con lo stesso formato del file di ingresso.
Il nome dei due file sia passato al programma sulla riga di comando.
1 # include < stdio .h >
# include < stdlib .h >
3 # define MAXR 20
# define MAXC 20
5 # define MAXSTR 255
void manipolazione ( FILE * dev ) ;
7 int leggi_file ( char * nome_file ) ;
int add ( int rs , int cs , int * sum ) ;
9 float matrix [ MAXR ][ MAXC ];
int r , c ;
11 int main ( int argc , char ** argv )
{
13 FILE * F ;
if ( argc ==3)
15 {
if ( leggi_file ( argv [1]) )
17 {
if (( F = fopen ( argv [2] , " w " ) ) == NULL )
19 {
printf ( " Il file % s non e ’ stato aperto .\ n " , argv [2]) ;
21 system ( " pause " ) ;
return 1;
23 }
manipolazione ( F ) ;
25 fclose ( F ) ;
}
27 else
{
29 printf ( " Il file % s non e ’ stato aperto .\ n " , argv [1]) ;
system ( " pause " ) ;
31 return 2;
}
33 }
else
35 {
printf ( " Comandi inseriti non validi .\ n % s file_input file_output \
n " , argv [0]) ;
37 system ( " pause " ) ;
return 3;
57
39 }
system ( " pause " ) ;
41 return 0;
}
43 int leggi_file ( char * nome_file )
{
45 FILE * f ;
int i , j ;
47 if (( f = fopen ( nome_file , " r " ) ) == NULL )
return 0;
49 fscanf (f , " % d % d " , &r , & c ) ;
for ( i =0; i < r ; i ++)
51 {
for ( j =0; j < c ; j ++)
53 {
if ( fscanf (f , " % f " , & matrix [ i ][ j ]) == EOF )
55 {
fclose ( f ) ;
57 return 0;
}
59 }
}
61 fclose ( f ) ;
return 1;
63 }
58
# include < stdio .h >
2 # include < stdlib .h >
# define MAXR 20
4 # define MAXC 20
# define MAXSTR 255
6 void manipolazione ( FILE * dev ) ;
int leggi_file ( char * nome_file ) ;
8 float sum ( int r , int c ) ;
float matrix [ MAXR +2][ MAXC +2];
10 int r , c ;
FILE * F ;
14 if ( argc ==3)
{
16 if ( leggi_file ( argv [1]) )
{
18 if (( F = fopen ( argv [2] , " w " ) ) == NULL )
{
20 printf ( " Il file % s non e ’ stato aperto .\ n " , argv [2]) ;
system ( " pause " ) ;
22 return 1;
}
24 manipolazione ( F ) ;
fclose ( F ) ;
26 }
else
28 {
printf ( " Il file % s non e ’ stato aperto .\ n " , argv [1]) ;
30 system ( " pause " ) ;
return 2;
32 }
}
34 else
{
36 printf ( " Comandi inseriti non validi .\ n % s file_input file_output \
n " , argv [0]) ;
system ( " pause " ) ;
38 return 3;
}
40 system ( " pause " ) ;
return 0;
42 }
FILE * f ;
46 int i , j ;
if (( f = fopen ( nome_file , " r " ) ) == NULL )
48 return 0;
fscanf (f , " % d % d " , &r , & c ) ;
50 for ( i =1; i <= r ; i ++)
{
52 for ( j =1; j <= c ; j ++)
{
54 if ( fscanf (f , " % f " , & matrix [ i ][ j ]) == EOF )
{
56 fclose ( f ) ;
return 0;
58 }
}
60 }
59
fclose ( f ) ;
62 return 1;
}
64 float sum ( int rc , int cc )
{
66 float s =0;
s += matrix [ rc -1][ cc -1];
68 s += matrix [ rc -1][ cc ];
s += matrix [ rc -1][ cc +1];
70 s += matrix [ rc ][ cc -1];
s += matrix [ rc ][ cc +1];
72 s += matrix [ rc +1][ cc -1];
s += matrix [ rc +1][ cc ];
74 s += matrix [ rc +1][ cc +1];
printf ( " sum (%2 d , %2 d ) =%2.0 f \ n " , rc , cc , s ) ;
76 return s ;
}
78 void manipolazione ( FILE * dev )
{
80 int i , j ;
fprintf ( dev , " % d % d \ n " , r , c ) ;
82 fprintf ( dev , " %.1 f " , sum (1 , 1) /3) ;
for ( j =2; j < c ; j ++)
84 fprintf ( dev , " %.1 f " , sum (1 , j ) /5) ;
if ( j !=2)
86 fprintf ( dev , " %.1 f \ n " , sum (1 , j ) /3) ;
for ( i =2; i < r ; i ++)
88 {
fprintf ( dev , " %.1 f " , sum (i , 1) /5) ;
90 for ( j =2; j < c ; j ++)
fprintf ( dev , " %.1 f " , sum (i , j ) /8) ;
92 if ( j !=2)
fprintf ( dev , " %.1 f \ n " , sum (i , j ) /5) ;
94 }
if ( i !=2)
96 {
fprintf ( dev , " %.1 f " , sum (i , 1) /3) ;
98 for ( j =2; j < c ; j ++)
fprintf ( dev , " %.1 f " , sum (i , j ) /5) ;
100 if ( j !=2)
fprintf ( dev , " %.1 f \ n " , sum (i , j ) /3) ;
102 }
}
Listing 4.3: Manipolazione di una matrice - III
Tale implementazione, a differenza della precedente, evita il controllo per ogni casella, semplicemente
mettendo un bordo esterno alla matrice. Si avrà un’utilizzo maggiore della memoria ma una riduzione
evidente degli if da valutare.
60
# include < stdio .h >
2 # include < stdlib .h >
# define path_in " text . txt "
4 # define path_out " text_out . txt "
# define row_lenght 20
6 char toLower ( char c )
{
8 if (c >= ’A ’ && c <= ’Z ’)
return ’a ’ + c - ’A ’;
10 else
return c ;
12 }
61
62
Capitolo 5
Esercitazione 2
• Dato un file di testo, l’applicazione individua anzitutto un’insieme di parole del testo (tipicamente
lunghe e ripetute) che verranno poi elaborate come segue.
• A ciascuna delle parole dell’insieme cosı̀ costruito viene associato un intero univoco, con valore
sempre compreso nell’intervallo [0 - 99]. L’applicazione riporta in un secondo file di testo ogni
associazione intero/parola (una per riga).
• Nel file di testo originale, infine, ogni parola che appartiene all’insieme viene sostituita dall’intero
ad essa associato (preceduto dal carattere $).
Si desidera un programma C che, dati due file (il file di testo trasformato e il corrispondente file delle
associazioni), ricostruisca il file di testo originale. Si può assumere che ogni riga del file “compresso” sia
lunga al più 80 caratteri.
Suggerimento: si usi un vettore di stringhe (realizzato come matrice di caratteri) per memorizzare
l’associazione tra un intero (indice del vettore, ovvero indice di riga della matrice) e la parola (elemento
del vettore, corrispondente a una riga della matrice) ad esso collegata. Il file di ingresso, inoltre, sia letto
per righe.
1 # include < stdio .h >
# include < string .h >
3 # include < stdlib .h >
# define ROWLENGHT 80
5 # define MAXINDEX 99
int decomprimi ( char * filename_input , char * filename_output , char corr [][
ROWLENGHT +1]) ;
7 int l e g g i _ c o r r i s p o n d e n z e ( char * filename , char corr [][ ROWLENGHT +1]) ;
int main ()
9 {
63
25 int decomprimi ( char * filename_input , char * filename_output , char corr [][
ROWLENGHT +1])
{
27 FILE * fin , * fout ;
char c ;
29 int num_c , corrispondenza ;
if (( fin = fopen ( filename_input , " r " ) ) == NULL || ( fout = fopen (
filename_output , " w " ) ) == NULL )
31 return 0;
corrispondenza = 0;
33 while ( fscanf ( fin , " % c " , & c ) != EOF )
{
35 if ( c == ’$ ’)
{
37 fscanf ( fin , " % d " , & num_c ) ;
printf ( " % s " , corr [ num_c ]) ;
39 }
else
41 printf ( " % c " , c ) ;
}
43 return 1;
}
45 int l e g g i _ c o r r i s p o n d e n z e ( char * filename , char corr [][ ROWLENGHT +1])
{
47 FILE * f ;
int n_c ;
49 for ( n_c =0; n_c < MAXINDEX +1; n_c ++)
corr [ n_c ][0]=0;
51 if (( f = fopen ( filename , " r " ) ) == NULL )
return 0;
53 while ( fscanf (f , " % d " , & n_c ) != EOF )
fscanf (f , " % s " , corr [ n_c ]) ;
55 fclose ( f ) ;
return 1;
57 }
Listing 5.1: Decompressione di un testo
• leggere da tastiera un numero indefinito di stringhe, ciascuna composta da soli caratteri alfanumerici
e di lunghezza massima pari a 30. La lettura termini quando viene introdotta la parola “stop”.
• indicare, per ciascuna stringa acquisita, se essa è “periodica” oppure no, ovvero costituita da un’u-
nica sotto-stringa che si ripete. Ad esempio, sono periodiche (con periodo indicato tra parentesi)
le seguenti sotto-stringhe: “zzzzz” (1), “k21k21” (3), “vivaviva” (4), etc. Si noti che una stringa
composta da un solo carattere, come anche una stringa in cui l’ultimo periodo non risulti concluso,
non deve essere considerata periodica.
# include < stdio .h >
2 # include < stdlib .h >
# include < ctype .h >
4 # include < string .h >
# define terminatore " stop "
6 # define LENGHTMAX 30
int periodo ( char str []) ;
8 int main ()
{
10 char str [ LENGHTMAX +1]={0};
64
int i , p , err =0;
12 do
{
14 printf ( " Stringa (% s per terminare ) ?\ t " , terminatore ) ;
scanf ( " % s " , str ) ;
16 if ( strcmp ( str , terminatore ) ==0)
continue ;
18 for ( i =0; str [ i ]!= ’ \0 ’; i ++)
if (! isalnum ( str [ i ]) )
20 err =1;
if ( err )
22 {
err =0;
24 continue ;
}
26 printf ( " La stringa % s " , str ) ;
p = periodo ( str ) ;
28 if (p >=1)
printf ( " e ’ periodica ( periodo % d ) .\ n " , p ) ;
30 else
printf ( " non e ’ periodica .\ n " ) ;
32 }
while ( strcmp ( str , terminatore ) !=0) ;
34 return 0;
}
36 int egualNcharacter ( char s1 [] , char s2 [] , int n )
{
38 int i ;
i =0;
40 while (*( s1 + i ) ==*( s2 + i ) && *( s1 + i ) != ’ \0 ’ && i < n )
i ++;
42 return i == n ;
}
44 int periodo ( char str [])
{
46 int i , j ;
int lenstr = strlen ( str ) ;
48 if ( lenstr ==1 || lenstr == LENGHTMAX )
return 0;
50 for ( i =1; i <= lenstr /2; i ++)
{
52 if ( lenstr % i ==0)
{
54 for ( j = i ; j < lenstr && egualNcharacter ( str , str +j , i ) ; j += i )
;
56 if ( j == lenstr )
return i ;
58 }
}
60 return 0;
}
Listing 5.2: Stringhe periodiche
65
• legga tale file (di nome predefinito), memorizzandone il contenuto in un’opportuna struttura dati.
• indichi sul video quali e quante sono le tratte aeree per le quali esistono sia il volo di andata che
quello di ritorno.
# include < stdio .h >
2 # include < stdlib .h >
# include < string .h >
4 # define SIGLA_LENGHT 3
# define MAX_FLIGHT 200
6 # define flight_file " voli . txt "
typedef struct flight_t
8 {
FILE * f ;
30 if (( f = fopen ( filename , " r " ) ) == NULL )
return 0;
32 * n_voli = 0;
while (* n_voli < MAX_FLIGHT && fscanf (f , " % s % s " , voli [* n_voli ].
departure , voli [* n_voli ]. arrival ) ==2)
34 * n_voli +=1;
fflush ( f ) ;
36 fclose ( f ) ;
return 1;
38 }
int i , j , ar =0;
42 for ( i =0; i < n_voli -1; i ++)
{
44 if ( voli [ i ]. arrival [0]==0)
continue ;
46 for ( j = i +1; j < n_voli ; j ++)
{
48 if ( voli [ j ]. arrival [0]==0)
continue ;
50 if ( strcmp ( voli [ i ]. departure , voli [ j ]. arrival ) ==0 && strcmp (
voli [ i ]. arrival , voli [ j ]. departure ) ==0)
{
52 ar ++;
printf ( " % s % s \ n " , voli [ i ]. departure , voli [ i ]. arrival ) ;
54 voli [ j ]. arrival [0] = voli [ j ]. departure [0] = 0;
}
66
56 }
}
58 printf ( " Totale connessioni A / R : % d \ n " , ar ) ;
}
Listing 5.3: Voli aerei
67
68
Capitolo 6
Esercitazione 3
69
31 t1 = clock () ;
printf ( " %14.3 f " , (( float ) ( t1 - t0 ) ) / CLOCKS_PER_SEC ) ;
33 t0 = clock () ;
for ( M =0; M < vM [ j ]; M ++)
35 {
genera_casuale (a , vN [ i ] , VMAX ) ;
37 bubble_sort (a , vN [ i ]) ;
}
39 t1 = clock () ;
printf ( " %11.3 f " , (( float ) ( t1 - t0 ) ) / CLOCKS_PER_SEC ) ;
41 t0 = clock () ;
for ( M =0; M < vM [ j ]; M ++)
43 {
genera_casuale (a , vN [ i ] , VMAX ) ;
45 selection_sort (a , vN [ i ]) ;
}
47 t1 = clock () ;
printf ( " %14.3 f " , (( float ) ( t1 - t0 ) ) / CLOCKS_PER_SEC ) ;
49 printf ( " \ n " ) ;
}
51 free ( a ) ;
}
53 system ( " pause " ) ;
return 0;
55 }
int * my_alloca ( int n_el )
57 {
int * vet ;
59 vet = ( int *) malloc ( n_el * sizeof ( int ) ) ;
return vet ;
61 }
void genera_casuale ( int * vet , int n_el , int v_max )
63 {
for ( n_el - -; n_el >=0; n_el - -)
65 {
vet [ n_el ] = rand () % v_max ;
67 }
}
69 void insersion_sort ( int * vet , int dim )
{
71 int i , j , temp ;
for ( i =1; i < dim ; i ++)
73 {
temp = vet [ i ];
75 for ( j =i -1; j >=0 && temp < vet [ j ]; j - -)
{
77 vet [ j +1] = vet [ j ];
}
79 vet [ j +1]= temp ;
}
81 }
void bubble_sort ( int * vet , int dim )
83 {
int i , j , temp , scambio ;
85 scambio =1;
for ( i = dim -1; i >1 && scambio ; i - -)
87 {
scambio =0;
89 for ( j =0; j < i ; j ++)
{
91 if ( vet [ j ] > vet [ j +1])
{
70
93 temp = vet [ j ];
vet [ j ] = vet [ j +1];
95 vet [ j +1] = temp ;
scambio =1;
97 }
}
99 }
}
101 void selection_sort ( int * vet , int dim )
{
103 int i , j , temp , min ;
for ( i =0; i < dim -2; i ++)
105 {
min = i ;
107 for ( j = i +1; j < dim -1; j ++)
{
109 if ( vet [ j ] < vet [ min ])
{
111 min = j ;
}
113 }
if ( min != i )
115 {
temp = vet [ i ];
117 vet [ i ] = vet [ min ];
vet [ min ] = temp ;
119 }
}
121 }
int i ;
125 for ( i =1; i < dim ; i ++)
if ( vet [ i ] < vet [i -1])
127 return 0;
return 1;
129 }
Listing 6.1: Confronto degli algoritmi di ordinamento
1. facendo uso di una matrice statica di caratteri per memorizzare le stringhe del file di ingresso
(assumendo che queste siano in numero al più pari a 100).
2. allocando dinamicamente il vettore in cui salvare le stringhe e le stesse stringhe all’atto della
memorizzazione.
Si noti che nel primo caso lo “scambio” tra due stringhe ai fini dell’ordinamento deve essere eseguito
tramite delle opportune chiamate alla funzione strcpy(), mentre nel secondo occorre solo scambiare i
relativi puntatori.
71
# include < stdio .h >
2 # include < stdlib .h >
# include < string .h >
4 # define path_in " testo . txt "
# define path_out " testo1 . txt "
6 # define lenMax 30
# define strMax 100
8 int leggi_file ( char * path , char matrix [][ lenMax +1] , int * n_stringhe ) ;
int salva_file ( char * path , char vet [][ lenMax +1] , int dim ) ;
10 void swap ( char mat [][ lenMax +1] , int i , int j ) ;
void bubble_sort ( char vet [][ lenMax +1] , int dim ) ;
12 void ordina ( char mat [][ lenMax +1] , int n_str ) ;
int main ()
14 {
int n_str ;
16 char mat [ strMax ][ lenMax +1];
if (! leggi_file ( path_in , mat , & n_str ) )
18 {
printf ( " Errore in apertura del file % s .\ n " , path_in ) ;
20 system ( " pause " ) ;
return 1;
22 }
ordina ( mat , n_str ) ;
24 if (! salva_file ( path_out , mat , n_str ) )
{
26 printf ( " Errore in apertura del file % s .\ n " , path_out ) ;
system ( " pause " ) ;
28 return 1;
}
30 system ( " pause " ) ;
return 0;
32 }
int leggi_file ( char * path , char matrix [][ lenMax +1] , int * n_stringhe )
34 {
FILE * f ;
36 * n_stringhe =0;
if (( f = fopen ( path , " r " ) ) == NULL )
38 return 0;
while ( fscanf (f , " % s " , matrix [* n_stringhe ]) != EOF )
40 * n_stringhe +=1;
return 1;
42 }
int salva_file ( char * path , char vet [][ lenMax +1] , int dim )
44 {
FILE * f ;
46 if (( f = fopen ( path , " w " ) ) == NULL )
return 0;
48 int i ;
for ( i =0; i < dim ; i ++)
50 fprintf (f , " % s \ n " , vet [ i ]) ;
fflush ( f ) ;
52 fclose ( f ) ;
return 1;
54 }
72
62 void bubble_sort ( char vet [][ lenMax +1] , int dim )
{
64 int i , j , scambio ;
scambio =1;
66 for ( i = dim -1; i >1 && scambio ; i - -)
{
68 scambio =0;
for ( j =0; j < i ; j ++)
70 {
if ( strcmp ( vet [ j ] , vet [ j +1]) >0)
72 {
swap ( vet , j , j +1) ;
74 scambio =1;
}
76 }
}
78 }
int n_str ;
15 char ** mat = leggi_file ( path_in , & n_str ) ;
if ( mat == NULL )
17 {
printf ( " Errore in apertura del file % s .\ n " , path_in ) ;
19 system ( " pause " ) ;
return 1;
21 }
ordina ( mat , n_str ) ;
23 if (! salva_file ( path_out , mat , n_str ) )
{
25 printf ( " Errore in apertura del file % s .\ n " , path_out ) ;
system ( " pause " ) ;
27 return 1;
}
29 free ( mat ) ;
system ( " pause " ) ;
31 return 0;
}
33 char ** leggi_file ( char * path , int * n_stringhe )
{
35 FILE * f ;
char ** matrix ;
37 matrix = NULL ;
* n_stringhe =0;
73
39 if (( f = fopen ( path , " r " ) ) == NULL )
return matrix ;
41 * n_stringhe =1;
matrix = ( char **) malloc ( sizeof ( char *) ) ;
43 matrix [0] = ( char *) malloc ( lenMax * sizeof ( char ) ) ;
while ( fscanf (f , " % s " , matrix [* n_stringhe -1]) != EOF )
45 {
* n_stringhe +=1;
47 matrix = ( char **) realloc ( matrix , * n_stringhe * sizeof ( char *) ) ;
matrix [* n_stringhe -1] = ( char *) malloc ( lenMax * sizeof ( char ) ) ;
49 }
fclose ( f ) ;
51 * n_stringhe -=1;
return matrix ;
53 }
void swap ( char ** mat , int i , int j )
55 {
char * a ;
57 a = mat [ i ];
mat [ i ] = mat [ j ];
59 mat [ j ] = a ;
}
61 void ordina ( char ** mat , int n_str )
{
63 bubble_sort ( mat , n_str ) ;
}
65 void bubble_sort ( char ** vet , int dim )
{
67 int i , j , scambio ;
scambio =1;
69 for ( i = dim -1; i >1 && scambio ; i - -)
{
71 scambio =0;
for ( j =0; j < i ; j ++)
73 {
if ( strcmp ( vet [ j ] , vet [ j +1]) >0)
75 {
swap ( vet , j , j +1) ;
77 scambio =1;
}
79 }
}
81 }
FILE * f ;
85 if (( f = fopen ( path , " w " ) ) == NULL )
return 0;
87 int i ;
for ( i =0; i < dim ; i ++)
89 fprintf (f , " % s \ n " , vet [ i ]) ;
fflush ( f ) ;
91 fclose ( f ) ;
return 1;
93 }
Listing 6.3: Ordinamento di stringhe con allocazione dinamica
74
Capitolo 7
Esercitazione 4
POSSIBILE VARIANTE Si trattino le lettere maiuscole e quelle minuscole come equivalenti (ovvero,
si considerino le stringhe “parola”, “PAROLA”, “ParolA”, etc., come corrispondenti).
# include < stdio .h >
2 # include < stdlib .h >
# include < string .h >
4 # include < ctype .h >
# define occorrenze_path " ricercare . txt "
6 # define file_path " file . txt "
# define max_word_lenght 20
8 # define VARIANTE 1
struct occorrenze
10 {
char * word ;
12 int number ;
};
14 struct occorrenze * read_words ( char * path , int * n_w ) ;
int ricerca ( char * el , struct occorrenze * list , int n_w ) ;
16 void up date_o ccorre nze ( struct occorrenze * list , int n_w , char * path ) ;
void print_occorrenze ( struct occorrenze * list , int n_w ) ;
18 void toLower ( char * str ) ;
int main ()
20 {
75
struct occorrenze * read_words ( char * path , int * n_w )
30 {
* n_w =0;
32 struct occorrenze * list ;
int i ;
34 char tmp [ max_word_lenght +1];
FILE * f ;
36 if (( f = fopen ( path , " r " ) ) == NULL )
return NULL ;
38 fscanf (f , " % d " , n_w ) ;
list = ( struct occorrenze *) malloc ((* n_w ) * sizeof ( struct occorrenze ) )
;
40 for ( i =0; i <* n_w ; i ++)
{
42 list [ i ]. number =0;
fscanf (f , " % s " , tmp ) ;
44 list [ i ]. word = strdup ( tmp ) ;
if ( VARIANTE )
46 toLower ( list [ i ]. word ) ;
}
48 fclose ( f ) ;
return list ;
50 }
void upd ate_o ccorre nze ( struct occorrenze * list , int n_w , char * path )
52 {
FILE * f ;
54 if (( f = fopen ( path , " r " ) ) == NULL )
;
56 char * t ;
int n_occorrenza ;
58 t = ( char *) malloc ( max_word_lenght * sizeof ( char ) ) ;
while ( fscanf (f , " % s " , t ) != EOF )
60 {
n_occorrenza = ricerca (t , list , n_w ) ;
62 if ( n_occorrenza != -1)
list [ n_occorrenza ]. number ++;
64 }
fclose ( f ) ;
66 }
int ricerca ( char * el , struct occorrenze * list , int n_w )
68 {
int i ;
70 if ( VARIANTE )
toLower ( el ) ;
72 for ( i =0; i < n_w ; i ++)
{
74 if ( strcmp ( el , list [ i ]. word ) ==0)
return i ;
76 }
return -1;
78 }
void print_occorrenze ( struct occorrenze * list , int n_w )
80 {
int i ;
82 for ( i =0; i < n_w ; i ++)
printf ( " % s - % d occorrenz % c \ n " , list [ i ]. word , list [ i ]. number , (
list [ i ]. number ==1 ? ’a ’ : ’e ’) ) ;
84 }
void toLower ( char * str )
86 {
int i ;
88 for ( i =0; str [ i ]!=0; i ++)
76
str [ i ] = tolower ( str [ i ]) ;
90 }
Listing 7.1: Occorrenza di parole
1. il programma limiti la visualizzazione di tali posizioni alle prime 10 occorrenze di ciascuna parola
(qualora una stringa venisse rintracciata più di 10 volte, il programma fornisca il conteggio esatto
delle occorrenze, ma riporti solo le prime 10 posizioni)
2. il programma visualizzi correttamente tutte le posizioni in cui ogni parola del primo file appare nel
secondo
Si noti che nel primo caso è sufficiente utilizzare, per ciascuna parola da rintracciare, un vettore statico, il
cui contenuto può essere generato durante la stessa fase di conteggio delle occorrenze. Nel secondo caso,
invece, a meno di ricorrere a qualche altra struttura dati, si deve utilizzare un vettore dinamico, allocato
e “riempito” solo dopo aver trovato il numero di occorrenze di ciascuna parola.
1 # include < stdio .h >
# include < stdlib .h >
3 # include < string .h >
# include < ctype .h >
5 # define occorrenze_path " ricercare . txt "
# define file_path " file . txt "
7 # define max_word_lenght 20
# define VARIANTE 1
9 # define limita_indice 10
struct occorrenze
11 {
char * word ;
13 int number , n_index ;
int * indice ;
15 };
77
if (( f = fopen ( path , " r " ) ) == NULL )
39 return NULL ;
fscanf (f , " % d " , n_w ) ;
41 list = ( struct occorrenze *) malloc ((* n_w ) * sizeof ( struct occorrenze ) )
;
for ( i =0; i <* n_w ; i ++)
43 {
list [ i ]. number =0;
45 list [ i ]. n_index =0;
list [ i ]. indice = NULL ;
47 fscanf (f , " % s " , tmp ) ;
list [ i ]. word = strdup ( tmp ) ;
49 if ( VARIANTE )
toLower ( list [ i ]. word ) ;
51 }
fclose ( f ) ;
53 return list ;
}
55 void upd ate_o ccorre nze ( struct occorrenze * list , int n_w , char * path )
{
57 FILE * f ;
if (( f = fopen ( path , " r " ) ) == NULL )
59 ;
char * t ;
61 int n_occorrenza , n_word =0;
t = ( char *) malloc ( max_word_lenght * sizeof ( char ) ) ;
63 while ( fscanf (f , " % s " , t ) != EOF )
{
65 n_word ++;
n_occorrenza = ricerca (t , list , n_w ) ;
67 if ( n_occorrenza != -1)
{
69 list [ n_occorrenza ]. number ++;
if ( limita_indice >0)
71 {
if ( list [ n_occorrenza ]. n_index != limita_indice )
73 {
list [ n_occorrenza ]. n_index ++;
75 list [ n_occorrenza ]. indice = ( int *) realloc ( list [
n_occorrenza ]. indice , ( list [ n_occorrenza ]. n_index
) * sizeof ( int ) ) ;
list [ n_occorrenza ]. indice [ list [ n_occorrenza ]. n_index
-1] = n_word ;
77 }
}
79 else
{
81 list [ n_occorrenza ]. n_index ++;
list [ n_occorrenza ]. indice = ( int *) realloc ( list [
n_occorrenza ]. indice , ( list [ n_occorrenza ]. n_index ) *
sizeof ( int ) ) ;
83 list [ n_occorrenza ]. indice [ list [ n_occorrenza ]. n_index -1]
= n_word ;
}
85 }
}
87 fclose ( f ) ;
}
89 int ricerca ( char * el , struct occorrenze * list , int n_w )
{
91 int i ;
if ( VARIANTE )
78
93 toLower ( el ) ;
for ( i =0; i < n_w ; i ++)
95 {
if ( strcmp ( el , list [ i ]. word ) ==0)
97 return i ;
}
99 return -1;
}
101 void print_occorrenze ( struct occorrenze * list , int n_w )
{
103 int i , j ;
for ( i =0; i < n_w ; i ++)
105 {
printf ( " % s - % d occorrenz % c " , list [ i ]. word , list [ i ]. number , (
list [ i ]. number ==1 ? ’a ’ : ’e ’) ) ;
107 if ( list [ i ]. n_index >0)
{
109 printf ( " parol % c : " , ( list [ i ]. n_index ==1 ? ’a ’ : ’e ’) ) ;
for ( j =0; j < list [ i ]. n_index ; j ++)
111 printf ( " % d " , list [ i ]. indice [ j ]) ;
}
113 printf ( " \ n " ) ;
}
115 }
int i ;
119 for ( i =0; str [ i ]!=0; i ++)
str [ i ] = tolower ( str [ i ]) ;
121 }
Listing 7.2: Occorrenze parole con indice analitico
79
int row_for_colums ( struct matrix * mat1 , struct matrix * mat2 , int r , int
c);
13 int main ( int argc , char * argv [])
{
15 if ( argc <4)
{
17 printf ( " Parametri insufficienti : % s matrix1_file matrix2_file
result_file \ n " , argv [0]) ;
return EXIT_FAILURE ;
19 }
FILE * f1 , * f2 , * f3 ;
21 if (( f1 = fopen ( argv [1] , " r " ) ) == NULL )
{
23 printf ( " Errore apertura % s \ n " , argv [1]) ;
return EXIT_FAILURE ;
25 }
if (( f2 = fopen ( argv [2] , " r " ) ) == NULL )
27 {
printf ( " Errore apertura % s \ n " , argv [2]) ;
29 fclose ( f1 ) ;
return EXIT_FAILURE ;
31 }
if (( f3 = fopen ( argv [3] , " w " ) ) == NULL )
33 {
printf ( " Errore apertura % s \ n " , argv [3]) ;
35 fclose ( f1 ) ;
fclose ( f2 ) ;
37 return EXIT_FAILURE ;
}
39 struct matrix mat [3]={{0}};
read_matrix (& mat [0] , f1 ) ;
41 read_matrix (& mat [1] , f2 ) ;
if ( is_multiplicate ( mat [0] , mat [1]) )
43 multiplicate (& mat [2] , & mat [0] , & mat [1]) ;
print_matrix ( mat [2] , f3 ) ;+
45 fclose ( f1 ) ;
fclose ( f2 ) ;
47 fflush ( f3 ) ;
fclose ( f3 ) ;
49 return 0;
}
51 void read_matrix ( struct matrix * mat , FILE * f )
{
53 fscanf (f , " % d % d " , & mat - >r , & mat - > c ) ;
mat - > value = ( int **) malloc (( mat - > r ) * sizeof ( int *) ) ;
55 int i , j ;
for ( i =0; i < mat - > r ; i ++)
57 {
mat - > value [ i ] = ( int *) malloc (( mat - > c ) * sizeof ( int ) ) ;
59 for ( j =0; j < mat - > c ; j ++)
fscanf (f , " % d " , & mat - > value [ i ][ j ]) ;
61 }
}
63 void print_matrix ( struct matrix mat , FILE * f )
{
65 fprintf (f , " % d % d \ n " , mat .r , mat . c ) ;
int i , j ;
67 for ( i =0; i < mat . r ; i ++)
{
69 for ( j =0; j < mat . c ; j ++)
fprintf (f , " % d " , mat . value [ i ][ j ]) ;
71 fprintf (f , " \ n " ) ;
80
}
73 }
int is_multiplicate ( struct matrix mat1 , struct matrix mat2 )
75 {
81
82
Capitolo 8
Esercitazione 5
Vincoli e Restrizioni La generazione dei numeri binari deve essere ottenuta senza eseguire alcuna
conversione di valori da decimale a binario.
Suggerimento Si utilizzi un vettore di N interi per rappresentare il numero binario (ogni elemento del
vettore corrisponda a un bit del numero binario). La funzione ricorsiva deve, a ogni passo della ricorsione,
assegnare il valore di uno dei bit del numero binario (si possono avere solo i casi 0 e 1). Per ognuno dei
due possibili valori, si deve poi ricorrere per “sistemare” i bit successivi. Al termine della ricorsione,
quindi, occorre visualizzare il numero cosı̀ ottenuto.
1 # include < stdio .h >
# include < malloc .h >
3 # include < string .h >
void disposizioni ( char * prefix , int n ) ;
5 void s ta m p a_ d i sp o s iz i o ni ( int n ) ;
int main ()
7 {
int n ;
9 printf ( " Inserisci il numero di bit :\ t " ) ;
scanf ( " % d " , & n ) ;
11 s t am p a _d i s po s i zi o n i ( n ) ;
return 0;
13 }
int s ;
17 if ( n ==0)
printf ( " % s \ n " , prefix ) ;
19 else
{
21 s = strlen ( prefix ) ;
prefix [ s ]= ’0 ’;
23 prefix [ s +1]= ’ \0 ’;
disposizioni ( prefix , n -1) ;
25 prefix [ s ]= ’1 ’;
prefix [ s +1]= ’ \0 ’;
27 disposizioni ( prefix , n -1) ;
}
29 }
void s ta m p a_ d i sp o s iz i o ni ( int n )
31 {
char * str ;
33 if (( str = ( char *) malloc (( n +1) * sizeof ( char ) ) ) == NULL )
83
return ;
35 str [0]= ’ \0 ’;
disposizioni ( str , n ) ;
37 }
Listing 8.1: Generazione di numeri binari
84
* n_righe =0;
45 return NULL ;
}
47 }
return res ;
49 }
void stampa_colonne ( char * path , char ** file , int n_righe )
51 {
int i ;
87 for ( i = strlen ( str ) -1; i >=0; i - -)
printf ( " % c " , str [ i ]) ;
89 printf ( " \ n " ) ;
}
Listing 8.2: Sviluppo di un sistema del totocalcio
85
essendo mij l’elemento della matrice in posizione [i, j] e ∆ij il determinante della matrice che si ottiene
cancellando la riga i e la colonna j dalla matrice di partenza.
Si noti che il valore di N non è noto: esso deve essere dedotto dal contenuto del file di ingresso.
# include < stdio .h >
2 # include < string .h >
# include < malloc .h >
4 # define PATH " matrice . txt "
float ** leggi_file ( char * path , int * dim ) ;
6 float determinante ( float ** matrice , int dim ) ;
float ** c o m p l e m e n t o _ a l g e b r i c o ( float ** matrice , int dim , int r , int c ) ;
8 int main ()
{
10 float ** matrice ;
int dim ;
12 if (( matrice = leggi_file ( PATH , & dim ) ) == NULL )
return -1;
14 printf ( " Il determinante della matrice nel file e ’ % f \ n " ,
determinante ( matrice , dim ) ) ;
return 0;
16 }
86
mtemp = c o m p l e m e n t o _ a l g e b r i c o ( matrice , dim , 0 , i ) ;
58 if ( mtemp == NULL )
exit ( EXIT_FAILURE ) ;
60 if ( i %2==0)
res += matrice [0][ i ]* determinante ( mtemp , dim -1) ;
62 else
res -= matrice [0][ i ]* determinante ( mtemp , dim -1) ;
64 free ( mtemp ) ;
}
66 return res ;
}
68 float ** leggi_file ( char * path , int * dim )
{
70 float ** mat ;
FILE * fp ;
72 * dim =0;
if (( fp = fopen ( path , " r " ) ) == NULL )
74 return NULL ;
int i , j ;
76 char c ;
while (( c = fgetc ( fp ) ) != EOF )
78 if ( c == ’\ n ’)
(* dim ) ++;
80 rewind ( fp ) ;
if (( mat = ( float **) malloc ((* dim ) * sizeof ( float *) ) ) == NULL )
82 {
fclose ( fp ) ;
84 return NULL ;
}
86 for ( i =0; i <(* dim ) ; i ++)
{
88 if (( mat [ i ] = ( float *) malloc ((* dim ) * sizeof ( float ) ) ) == NULL )
{
90 for (i - -; i >=0; i - -)
free ( mat [ i ]) ;
92 free ( mat ) ;
return NULL ;
94 }
for ( j =0; j <(* dim ) ; j ++)
96 fscanf ( fp , " % f " , & mat [ i ][ j ]) ;
}
98 fclose ( fp ) ;
return mat ;
100 }
Listing 8.3: Determinante di una matrice
87
88
Capitolo 9
Esercitazione 6
• acquisisca da file un numero indefinito di punti del piano cartesiano, memorizzandoli in un vettore
di strutture (“punto”) allocato dinamicamente. Le coordinate dei punti sono riportate nel file di
ingresso su righe consecutive, in ragione di una riga del file per punto cartesiano.
• determini:
• ordini (mediante un algoritmo noto) i punti memorizzati nel vettore per valori crescenti di distanza
dall’origine del piano. Il vettore cosı̀ ordinato sia salvato su un file di uscita.
Vincoli e restrizioni Si richiede che il programma venga realizzato su più file, con struttura modulare,
come suggerito nel §3.1 del Sedgewick. Più in dettaglio, si preveda un modulo per la gestione del tipo
“punto”, composto da interfaccia e implementazione, contenente la dichiarazione del tipo stesso e le
funzioni base per operare su questo tipo di dato (ad esempio, per eseguire la lettura di un punto, calcolare
la distanza tra due punti, visualizzare le coordinate di un dato punto, etc.), e un modulo “client” che,
utilizzando tali funzioni, soddisfi le richieste dell’esercizio.
# include < stdio .h >
2 # include " point . h "
# define path_in " punti . txt "
4 # define path_out " punti_ordinati . txt "
void vfree ( point ** lista , int n_punti ) ;
6 point ** re ad _p oi nt _b y_ fi le ( char * path , int * n_p ) ;
int * c o p p i a _ m i n i m a _ d i s t a n z a ( point ** lista , int n_punti ) ;
8 int * c o p p i a _ m a s s i m a _ d i s t a n z a ( point ** lista , int n_punti ) ;
int s e g m e n t i _ d i s t a n z a _ m i n o r e ( point ** lista , int n_punti , float d ) ;
10 int ordina ( point ** lista , int n_punti , char * path ) ;
void quickSort ( point ** lista , int l , int r ) ;
12 int partition ( point ** lista , int l , int r ) ;
int main ()
14 {
89
24 scanf ( " % f " , & d ) ;
} while (d <=0) ;
26 if (( min = c o p p i a _ m i n i m a _ d i s t a n z a ( lista , n_punti ) ) == NULL )
{
28 printf ( " Errore allocazione dimanica della memoria .\ n " ) ;
vfree ( lista , n_punti ) ;
30 }
if (( max = c o p p i a _ m a s s i m a _ d i s t a n z a ( lista , n_punti ) ) == NULL )
32 {
printf ( " Errore allocazione dimanica della memoria .\ n " ) ;
34 free ( min ) ;
vfree ( lista , n_punti ) ;
36 }
n_segmenti = s e g m e n t i _ d i s t a n z a _ m i n o r e ( lista , n_punti , d ) ;
38 printf ( " La coppia dei punti piu ’ vicini tra loro e ’ composta dai
punti \ n \ t " ) ;
pPrint_to_stream ( lista [ min [0]] , stdout ) ;
40 printf ( " \ n \ t " ) ;
pPrint_to_stream ( lista [ min [1]] , stdout ) ;
42 printf ( " \ n " ) ;
printf ( " La coppia dei punti piu ’ lontani tra loro e ’ composta dai
punti \ n \ t " ) ;
44 pPrint_to_stream ( lista [ max [0]] , stdout ) ;
printf ( " \ n \ t " ) ;
46 pPrint_to_stream ( lista [ max [1]] , stdout ) ;
printf ( " \ n " ) ;
48 if ( n_segmenti ==0)
printf ( " Non sono presenti segmenti con lunghezza inferiore a % f \
n", d);
50 else if ( n_segmenti ==1)
printf ( " E ’ presente un segmento con lunghezza inferiore a % f \ n " ,
d);
52 else
printf ( " Sono presenti % d segmenti con lunghezza inferiore a % f \ n
" , n_segmenti , d ) ;
54 ordina ( lista , n_punti , path_out ) ;
free ( min ) ;
56 free ( max ) ;
vfree ( lista , n_punti ) ;
58 return 0;
}
60 void vfree ( point ** lista , int n_punti )
{
62 int i ;
for ( i =0; i < n_punti ; i ++)
64 free ( lista [ i ]) ;
free ( lista ) ;
66 }
point ** re ad _p oi nt _b y_ fi le ( char * path , int * n_p )
68 {
FILE * f ;
70 point ** res ;
char c ;
72 int i ;
* n_p =0;
74 if (( f = fopen ( path , " r " ) ) == NULL )
{
76 printf ( " Errore lettura file di ingresso % s \ n " , path ) ;
return NULL ;
78 }
while (( c = fgetc ( f ) ) != EOF )
80 if ( c == ’\ n ’)
90
(* n_p ) ++;
82 if (* n_p ==0)
{
84 printf ( " Non sono presenti punti nel file .\ n " ) ;
return NULL ;
86 }
if (( res = ( point **) malloc ((* n_p ) * sizeof ( point *) ) ) == NULL )
88 {
printf ( " Errore allocazione dinamica della memoria .\ n " ) ;
90 fclose ( f ) ;
return NULL ;
92 }
rewind ( f ) ;
94 for ( i =0; i <(* n_p ) ; i ++)
if (( res [ i ]= pRead_by_stream ( f ) ) == NULL )
96 {
printf ( " Errore allocazione dinamica della memoria .\ n " ) ;
98 for (i - -; i >=0; i - -)
free ( res [ i ]) ;
100 free ( res ) ;
return NULL ;
102 }
return res ;
104 }
int * c o p p i a _ m i n i m a _ d i s t a n z a ( point ** lista , int n_punti )
106 {
int i , j , * res ;
108 double dmin = -1.0 , dcorr ;
if (( res =( int *) malloc (2* sizeof ( int ) ) ) == NULL )
110 {
printf ( " Errore allocazione dinamica della memoria .\ n " ) ;
112 return NULL ;
}
114 for ( i =0; i < n_punti -1; i ++)
for ( j = i +1; j < n_punti ; j ++)
116 {
dcorr = pDistanza ( lista [ i ] , lista [ j ]) ;
118 if ( dmin == -1.0 || dcorr < dmin )
{
120 res [0] = i ;
res [1] = j ;
122 dmin = dcorr ;
}
124 }
return res ;
126 }
int * c o p p i a _ m a s s i m a _ d i s t a n z a ( point ** lista , int n_punti )
128 {
int i , j , * res ;
130 double dmax = -1.0 , dcorr ;
if (( res =( int *) malloc (2* sizeof ( int ) ) ) == NULL )
132 {
printf ( " Errore allocazione dinamica della memoria .\ n " ) ;
134 return NULL ;
}
136 for ( i =0; i < n_punti -1; i ++)
{
138 for ( j = i +1; j < n_punti ; j ++)
{
140 dcorr = pDistanza ( lista [ i ] , lista [ j ]) ;
{
142 res [0] = i ;
91
res [1] = j ;
144 dmax = dcorr ;
}
146 }
}
148 return res ;
}
150 int s e g m e n t i _ d i s t a n z a _ m i n o r e ( point ** lista , int n_punti , float d )
{
152 d = d*d;
int i , j , res ;
154 for ( i = res =0; i < n_punti -1; i ++)
for ( j = i +1; j < n_punti ; j ++)
156 {
if (( lista [ i ] - >x - lista [ j ] - > x ) *( lista [ i ] - >x - lista [ j ] - > x ) +(
lista [ i ] - >y - lista [ j ] - > y ) *( lista [ i ] - >y - lista [ j ] - > y ) <d )
158 res ++;
}
160 return res ;
}
162 int ordina ( point ** lista , int n_punti , char * path )
{
164 FILE * f ;
if (( f = fopen ( path , " w " ) ) == NULL )
166 {
printf ( " Errore apertura file di output % s \ n " , path ) ;
168 return 0;
}
170 quickSort ( lista , 0 , n_punti -1) ;
int i ;
172 for ( i =0; i < n_punti ; i ++)
{
174 pPrint_to_stream ( lista [ i ] , f ) ;
fprintf (f , " \ n " ) ;
176 }
fflush ( f ) ;
178 fclose ( f ) ;
return 1;
180 }
void quickSort ( point ** lista , int l , int r )
182 {
int p ;
184 if (l < r )
{
186 p = partition ( lista , l , r ) ;
quickSort ( lista , l , p ) ;
188 quickSort ( lista , p +1 , r ) ;
}
190 }
int partition ( point ** lista , int l , int r )
192 {
point * temp ;
194 int i , j ;
i = l -1;
196 j = r +1;
while (i < j )
198 {
while ( pCompareModulo ( lista [++ i ] , lista [ l ]) <0)
200 ;
while ( pCompareModulo ( lista [ - - j ] , lista [ l ]) >0)
202 ;
if (i < j )
92
204 {
temp = lista [ i ];
206 lista [ i ] = lista [ j ];
lista [ j ] = temp ;
208 }
}
210 return j ;
}
Listing 9.1: Punti del piano (client.c)
# include < malloc .h >
2 # include < math .h >
typedef struct point_t
4 {
float x , y ;
6 } point ;
point * pRead_by_stream ( FILE * stream ) ;
8 point * pRead_by_string ( char * str ) ;
float pDistanza ( point * p1 , point * p2 ) ;
10 float pModulo ( point * p ) ;
int pCompareModulo ( point * p1 , point * p2 ) ;
12 int p C o m p a r e M o d u l o _ c o n _ r i f e r i m e n t o ( point * p1 , point * p2 , point * rif ) ;
int pPrint_to_stream ( point *p , FILE * f ) ;
14 int p P r i n t _ t o _ s t r e a m _ w i t h _ p r e c i s i o n ( point *p , FILE *f , int cifre ) ;
Listing 9.2: Punti del piano (point.h)
1 # include < stdio .h >
# include " point . h "
3 point ORIGINE = {0 , 0};
point * pRead_by_stream ( FILE * stream )
5 {
if ( stream == NULL )
7 return NULL ;
point * res ;
9 if (( res = malloc ( sizeof ( point ) ) ) == NULL )
return NULL ;
11 fscanf ( stream , " % f % f " , & res - >x , & res - > y ) ;
return res ;
13 }
if ( str == NULL )
17 return NULL ;
point * res ;
19 if (( res = malloc ( sizeof ( point ) ) ) == NULL )
return NULL ;
21 sscanf ( str , " % f % f " , & res - >x , & res - > y ) ;
return res ;
23 }
double dx , dy ;
27 dx = p1 - >x - p2 - > x ;
dy = p1 - >y - p2 - > y ;
29 return sqrt (( dx * dx ) +( dy * dy ) ) ;
}
31 float pModulo ( point * p )
{
33 return pDistanza (p , & ORIGINE ) ;
}
93
35 int pCompareModulo ( point * p1 , point * p2 )
{
37 return pModulo ( p1 ) - pModulo ( p2 ) ;
}
39 int p C o m p a r e M o d u l o _ c o n _ r i f e r i m e n t o ( point * p1 , point * p2 , point * rif )
{
41 return pDistanza ( p1 , rif ) - pDistanza ( p2 , rif ) ;
}
43 int pPrint_to_stream ( point *p , FILE * f )
{
45 return p P r i n t _ t o _ s t r e a m _ w i t h _ p r e c i s i o n (p , f , 2) ;
}
47 int p P r i n t _ t o _ s t r e a m _ w i t h _ p r e c i s i o n ( point *p , FILE *f , int cifre )
{
49 if ( f == NULL )
return 0;
51 fprintf (f , " %.* f %.* f " , cifre , p - >x , cifre , p - > y ) ;
return 1;
53 }
Listing 9.3: Punti del piano (point.c)
Vincoli e restrizioni Si richiede che il modulo dei dati contenga le funzioni per manipolare solo un
singolo dato (o, al più, due dati nel caso del confronto), senza “sapere” che tali dati sono memorizzati in
un vettore. Ad esempio, è illecito definire in questo modulo una funzione che esegua per intero la lettura
del file di ingresso e restituisca il vettore contenente i dati. Tale operazione deve essere opportunamente
eseguita all’interno del modulo principale (il main).
1 # include < stdio .h >
# include < string .h >
3 # include " item . h "
# define path_file " item . txt "
5 # define MAX 10
void v_free ( ITEM ** lista , int n_item ) ;
7 int binary_find ( ITEM ** lista , int l , int r , ITEM * el ) ;
ITEM ** leggi_da_file ( char * path , int * n_item ) ;
9 int main ()
{
11 int n_item , index , fine ;
ITEM ** lista , * iAtt ;
13 char cmd [ MAX ];
if (( lista = leggi_da_file ( path_file , & n_item ) ) == NULL )
15 return 1;
fine =0;
17 while (! fine )
{
94
19 scanf ( " % s " , cmd ) ;
if ( strcmp ( cmd , " find " ) ==0)
21 {
iAtt = iRead_By_stream ( stdin ) ;
23 if ( iAtt != NULL )
{
25 index = binary_find ( lista , 0 , n_item -1 , iAtt ) ;
if ( index == -1)
27 printf ( " Elemento non trovato .\ n " ) ;
else
29 printf ( " Elemento trovato in posizione % d .\ n " , index )
;
free ( iAtt ) ;
31 }
}
33 else if ( strcmp ( cmd , " exit " ) ==0)
fine =1;
35 else
printf ( " Comando non riconosciuto .\ n " ) ;
37 }
v_free ( lista , n_item ) ;
39 return 0;
}
41 void v_free ( ITEM ** lista , int n_item )
{
43 if ( lista == NULL )
return ;
45 for ( n_item - -; n_item >=0; n_item - -)
free ( lista [ n_item ]) ;
47 free ( lista ) ;
}
49 int binary_find ( ITEM ** lista , int l , int r , ITEM * el )
{
51 int m ;
if (l > r )
53 return -1;
m = ( l + r ) /2;
55 if ( iCompare ( el , lista [ m ]) ==0)
return m ;
57 else if ( iCompare ( el , lista [ m ]) <0)
return binary_find ( lista , l , m -1 , el ) ;
59 else
return binary_find ( lista , m +1 , r , el ) ;
61 }
ITEM ** leggi_da_file ( char * path , int * n_item )
63 {
FILE * f ;
65 ITEM ** res ;
char c ;
67 int i ;
* n_item =0;
69 if ( path == NULL )
return NULL ;
71 if (( f = fopen ( path , " r " ) ) == NULL )
{
73 printf ( " Errore apertura file .\ n " ) ;
return NULL ;
75 }
while (( c = fgetc ( f ) ) != EOF )
77 if ( c == ’\ n ’)
(* n_item ) ++;
79 rewind ( f ) ;
95
if (* n_item ==0)
81 {
printf ( " Non sono presenti item nel file % s \ n " , path ) ;
83 fclose ( f ) ;
return NULL ;
85 }
if (( res =( ITEM **) malloc ((* n_item ) * sizeof ( ITEM *) ) ) == NULL )
87 {
printf ( " Errore allocazione dinamica della memoria .\ n " ) ;
89 * n_item = 0;
fclose ( f ) ;
91 return NULL ;
}
93 for ( i =0; i <(* n_item ) ; i ++)
{
95 res [ i ] = iRead_By_stream ( f ) ;
if ( res [ i ]== NULL )
97 {
printf ( " Errore allocazione dinamica della memoria .\ n " ) ;
99 for (i - -; i >=0; i - -)
free ( res [ i ]) ;
101 free ( res ) ;
* n_item =0;
103 fclose ( f ) ;
return NULL ;
105 }
}
107 fclose ( f ) ;
return res ;
109 }
Listing 9.4: Ricerche dicotomiche (client.c)
1 # include < malloc .h >
typedef struct
3 {
int num ;
5 } ITEM ;
ITEM * iRead_By_stream ( FILE * stream ) ;
7 ITEM * iRead_By_string ( char * string ) ;
int iPrint ( ITEM *i , FILE * stream ) ;
9 int iCompare ( ITEM * i1 , ITEM * i2 ) ;
Listing 9.5: Ricerche dicotomiche (item.h)
1 # include < stdio .h >
# include " item . h "
3 ITEM * iRead_By_stream ( FILE * stream )
{
5 if ( stream == NULL )
return NULL ;
7 ITEM * res ;
if (( res =( ITEM *) malloc ( sizeof ( ITEM ) ) ) == NULL )
9 return NULL ;
fscanf ( stream , " % d " , & res - > num ) ;
11 return res ;
}
13 ITEM * iRead_By_string ( char * string )
{
15 if ( string == NULL )
return NULL ;
17 ITEM * res ;
96
if (( res =( ITEM *) malloc ( sizeof ( ITEM ) ) ) == NULL )
19 return NULL ;
sscanf ( string , " % d " , & res - > num ) ;
21 return res ;
}
23 int iPrint ( ITEM *i , FILE * stream )
{
25 if ( i == NULL || stream == NULL )
return 0;
27 fprintf ( stream , " % d " , i - > num ) ;
return 1;
29 }
• ordini tale vettore in maniera “crescente”, utilizzando uno dei seguenti algoritmi:
– merge sort
– quick sort
– heap sort
Si confrontino le prestazioni dei tre algoritmi per i valori di M = 10, 100, 1000 e di N = 1000, 5000, 10000,
misurandone i tempi di esecuzione. Si confrontino, inoltre, le prestazioni di tali algoritmi rispetto a quelle
degli algoritmi iterativi.
Il programma deve essere composto da tre moduli distinti:
Si supponga che il tipo di dato base “item” memorizzato nel vettore possa essere (a scelta):
• un numero intero.
# include < stdio .h >
2 # include < time .h >
# include " sort . h "
4 void vfree ( ITEM ** lista , int n ) ;
ITEM ** my_alloca ( int n_el ) ;
6 void genera_casuale ( ITEM ** vet , int n_el ) ;
int main ()
8 {
ITEM ** lista ;
10 int t0 , t1 ;
int i , j , M ;
12 int vN [] = {1000 , 5000 , 10000};
97
int vM [] = {10 , 100 , 1000};
14 printf ( " N M merge_sort quick_sort \ n " ) ;
for ( i =0; i < sizeof ( vN ) / sizeof ( int ) ; i ++)
16 {
lista = my_alloca ( vN [ i ]) ;
18 for ( j =0; j < sizeof ( vM ) / sizeof ( int ) ; j ++)
{
20 printf ( " %6 d %6 d " , vN [ i ] , vM [ j ]) ;
t0 = clock () ;
22 for ( M =0; M < vM [ M ]; M ++)
{
24 genera_casuale ( lista , vN [ i ]) ;
merge_sort ( lista , 0 , vN [ i ] -1) ;
26 vfree ( lista , vN [ i ]) ;
}
28 t1 = clock () ;
printf ( " %10.3 f " , (( float ) ( t1 - t0 ) ) / CLOCKS_PER_SEC ) ;
30 t0 = clock () ;
for ( M =0; M < vM [ M ]; M ++)
32 {
genera_casuale ( lista , vN [ i ]) ;
34 quick_sort ( lista , 0 , vN [ i ] -1) ;
vfree ( lista , vN [ i ]) ;
36 }
t1 = clock () ;
38 printf ( " %10.3 f " , (( float ) ( t1 - t0 ) ) / CLOCKS_PER_SEC ) ;
printf ( " \ n " ) ;
40 }
}
42 free ( lista ) ;
system ( " pause " ) ;
44 return 0;
}
46 void vfree ( ITEM ** lista , int n )
{
48 if ( lista == NULL )
return ;
50 for (n - -; n >=0; n - -)
free ( lista [ n ]) ;
52 }
ITEM ** vet ;
56 vet = ( ITEM **) malloc ( n_el * sizeof ( ITEM *) ) ;
return vet ;
58 }
98
9 int iPrint ( ITEM *i , FILE * stream ) ;
int iCompare ( ITEM * i1 , ITEM * i2 ) ;
11 int iCopy ( ITEM * i1 , ITEM * i2 ) ;
ITEM * iInit_rand () ;
Listing 9.8: Confronto tra algoritmi di ordinamento (item.h)
# include < stdio .h >
2 # include < time .h >
# include " item . h "
4 # define __i_rand_max RAND_MAX
ITEM * iRead_By_stream ( FILE * stream )
6 {
if ( stream == NULL )
8 return NULL ;
ITEM * res ;
10 if (( res =( ITEM *) malloc ( sizeof ( ITEM ) ) ) == NULL )
return NULL ;
12 fscanf ( stream , " % d " , & res - > num ) ;
return res ;
14 }
if ( string == NULL )
18 return NULL ;
ITEM * res ;
20 if (( res =( ITEM *) malloc ( sizeof ( ITEM ) ) ) == NULL )
return NULL ;
22 sscanf ( string , " % d " , & res - > num ) ;
return res ;
24 }
ITEM * iInit_rand ()
44 {
ITEM * res ;
46 if (( res = ( ITEM *) malloc ( sizeof ( ITEM ) ) ) == NULL )
return NULL ;
48 res - > num = rand () % __i_rand_max ;
return res ;
50 }
Listing 9.9: Confronto tra algoritmi di ordinamento (item.c)
# include " item . h "
2 int merge_sort ( ITEM ** vec , int l , int r ) ;
99
void quick_sort ( ITEM ** vec , int l , int r ) ;
Listing 9.10: Confronto tra algoritmi di ordinamento (sort.h)
# include " sort . h "
2 int Merge ( ITEM ** vec , int l , int m , int r )
{
4 int i , j , k ;
ITEM ** tmp ;
6 if (( tmp =( ITEM **) malloc (( r +1) * sizeof ( ITEM *) ) ) == NULL )
return 0;
8 i=l;
j = m +1;
10 k=l;
while (i <= m && j <= r )
12 {
if ( iCompare ( vec [ i ] , vec [ j ]) <0)
14 tmp [ k ++] = vec [ i ++];
else
16 tmp [ k ++] = vec [ j ++];
}
18 while (i <= m )
tmp [ k ++] = vec [ i ++];
20 while (j <= r )
tmp [ k ++] = vec [ j ++];
22 for ( k = l ; k <= r ; k ++)
vec [ k ] = tmp [ k ];
24 return 1;
}
26 int merge_sort ( ITEM ** vec , int l , int r )
// add a return value because dynamic allocator can be falliure
28 {
int m ;
30 if (l < r )
{
32 m = ( l + r ) /2;
merge_sort ( vec , l , m ) ;
34 merge_sort ( vec , m +1 , r ) ;
Merge ( vec , l , m , r ) ;
36 }
return 1;
38 }
int i , j ;
42 ITEM * tmp ;
i = l -1;
44 j = r +1;
while (i < j )
46 {
while ( iCompare ( vec [ - - j ] , vec [ l ]) >0)
48 ;
while ( iCompare ( vec [++ i ] , vec [ l ]) <0)
50 ;
if (i < j )
52 {
tmp = vec [ i ];
54 vec [ i ] = vec [ j ];
vec [ j ] = tmp ;
56 }
}
58 return j ;
100
}
60 void quick_sort ( ITEM ** vec , int l , int r )
{
62 int p ;
if (l < r )
64 {
p = partition ( vec , l , r ) ;
66 quick_sort ( vec , l , p ) ;
quick_sort ( vec , p +1 , r ) ;
68 }
}
Listing 9.11: Confronto tra algoritmi di ordinamento (sort.c)
101
102
Capitolo 10
Esercitazione 7
103
10 {
Queue * coda = NULL ;
12 Item * item ;
int choose ;
14 int fine =0;
int errori ;
16 char path [ MAX_PATH ]={0};
FILE * f ;
18 while (! fine )
{
20 errori =0;
choose = menu () ;
22 switch ( choose )
{
24 case 1:
delete_queue ( coda ) ;
26 if (( coda = new_queue () ) == NULL )
errori += printf ( " Errore nell ’ allocazione della
memoria .\ n " ) ;
28 break ;
case 2:
30 if (( item = i_read_by_stream ( stdin ) ) == NULL )
errori += printf ( " Errore nell ’ allocazione della
memoria .\ n " ) ;
32 if (! enqueue ( coda , item ) )
errori += printf ( " Errore nell ’ inserimento dell ’ item .\ n
");
34 break ;
case 3:
36 if (( item = dequeue ( coda ) ) == NULL )
errori += printf ( " Errore nell ’ estrazione dell ’ item .\ n "
);
38 else
{
40 errori =1;
printf ( " Valore estratto : " ) ;
42 i_print ( item , stdout ) ;
printf ( " \ n " ) ;
44 i_free ( item ) ;
}
46 break ;
case 4:
48 errori =1;
print_adt_stream ( coda , stdout ) ;
50 break ;
case 5:
52 printf ( " Introduci il percorso nel quale salvare la
struttura dati :\ t " ) ;
scanf ( " % s " , path ) ;
54 if (( f = fopen ( path , " w " ) ) != NULL )
{
56 print_adt_stream ( coda , f ) ;
fflush ( f ) ;
58 fclose ( f ) ;
}
60 break ;
case 6:
62 printf ( " Introduci il percorso dal quale leggere la
struttura dati :\ t " ) ;
scanf ( " % s " , path ) ;
64 if (( f = fopen ( path , " r " ) ) != NULL )
{
104
66 if ( is_empty_queue ( coda ) )
{
68 delete_queue ( coda ) ;
if (( coda = new_queue () ) == NULL )
70 printf ( " Errore nell ’ allocazione della
memoria .\ n " ) ;
}
72 if (! read_adt_stream ( coda , f ) )
{
74 errori = 1;
printf ( " Errore nella lettura dell ’ ADT dal file \ n
");
76 }
fclose ( f ) ;
78 }
break ;
80 case 7:
delete_queue ( coda ) ;
82 fine =1;
}
84 if ( errori )
pausa ;
86 }
return 0;
88 }
int read_adt_stream ( Queue * coda , FILE * stream )
90 {
Item * item ;
92 while ( fgetc ( stream ) != ’\ n ’)
;
94 while (( item = i_read_by_stream ( stream ) ) != NULL )
if (! enqueue ( coda , item ) )
96 return 0;
return 1;
98 }
void print_adt_stream ( Queue * coda , FILE * stream )
100 {
Item * item ;
102 if ( is_empty_queue ( coda ) )
fprintf ( stream , " Struttura vuota .\ n " ) ;
104 else
{
106 fprintf ( stream , " Valori estratti :\ n " ) ;
while (( item = dequeue ( coda ) ) != NULL )
108 {
fprintf ( stream , " \ t " ) ;
110 i_print ( item , stream ) ;
fprintf ( stream , " \ n " ) ;
112 i_free ( item ) ;
}
114 }
}
116 int menu ()
{
118 int choose ;
pulisci ;
120 do
{
122 printf ( " \ nSeleziona l ’ operazione desiderata :\ n \ t1 - Creazione
nuova Queue \ n \ t2 - Inserimento di un nuovo elemento \ n \ t3 -
Estrazione e stampa di un elemento \ n \ t4 - Visualizzazione a
video di tutti gli elementi \ n \ t5 - Salvataggio su file di
105
tutti gli elementi \ n \ t6 - Caricamento degli elementi da file \
n \ t7 - Esci \ nIntroduci la scelta desiderata :\ t " ) ;
scanf ( " % d " , & choose ) ;
124 if ( choose <1 || choose >7)
{
126 pulisci ;
printf ( " \ tERRORE IMMISSIONE DATI ! " ) ;
128 }
}
130 while ( choose <1 || choose >7) ;
return choose ;
132 }
Listing 10.1: Queue (client.c)
1 # include < stdio .h >
# include < malloc .h >
3 typedef int Item ;
void i_print ( Item * item , FILE * stream ) ;
5 void i_free ( Item * item ) ;
Item * i_read_by_stream ( FILE * stream ) ;
Listing 10.2: Queue (item.h)
1 # include " item . h "
void i_print ( Item * item , FILE * stream )
3 {
if ( item == NULL )
9 return ;
free ( item ) ;
11 }
Item * res ;
15 if (( res =( Item *) malloc ( sizeof ( Item ) ) ) == NULL || stream == NULL )
return NULL ;
17 if ( stream == stdin )
printf ( " Introduci un numero intero :\ t " ) ;
19 if ( fscanf ( stream , " % d " , res ) !=1)
{
21 free ( res ) ;
return NULL ;
23 }
return res ;
25 }
Listing 10.3: Queue (item.c)
Per la versione con allocazione dinamica saranno necessari questi altri due listati
1 # include < stdio .h >
# include < malloc .h >
3 # include " item . h "
struct queue_item_t
5 {
Item * item ;
7 struct queue_item_t * next ;
};
9 typedef struct Queue_t
106
{
11 struct queue_item_t * head , * tail ;
} Queue ;
13 Queue * new_queue () ;
int is_empty_queue ( Queue * queue ) ;
15 int enqueue ( Queue * queue , Item * add ) ;
Item * dequeue ( Queue * queue ) ;
17 void print_dequeue ( Queue * queue , FILE * stream ) ;
void clean_queue ( Queue * queue ) ;
19 void delete_queue ( Queue * queue ) ;
Listing 10.4: Queue (queue.h)
# include < stdio .h >
2 # include " queue . h "
Queue * new_queue ()
4 {
Queue * res ;
6 if (( res = ( Queue *) malloc ( sizeof ( Queue ) ) ) == NULL )
return NULL ;
8 res - > head = NULL ;
res - > tail = NULL ;
10 return res ;
}
12 int is_empty_queue ( Queue * queue )
{
14 return queue == NULL || ( queue - > tail == NULL && queue - > head == NULL ) ;
}
16 int enqueue ( Queue * queue , Item * add )
{
18 if ( queue == NULL )
return 0;
20 struct queue_item_t * to_add ;
if (( to_add =( struct queue_item_t *) malloc ( sizeof ( struct queue_item_t )
) ) == NULL )
22 return 0;
to_add - > item = add ;
24 to_add - > next = NULL ;
if ( queue - > tail != NULL )
26 queue - > tail - > next = to_add ;
queue - > tail = to_add ;
28 if ( queue - > head == NULL )
queue - > head = to_add ;
30 printf ( " enqueue (... , " ) ;
i_print ( add , stdout ) ;
32 printf ( " ) \ n " ) ;
return 1;
34 }
if ( is_empty_queue ( queue ) )
38 return NULL ;
struct queue_item_t * head ;
40 Item * item ;
head = queue - > head ;
42 queue - > head = queue - > head - > next ;
if ( queue - > head == NULL )
44 queue - > tail = NULL ;
item = head - > item ;
46 free ( head ) ;
return item ;
48 }
107
void print_dequeue ( Queue * queue , FILE * stream )
50 {
Item * extract ;
52 extract = dequeue ( queue ) ;
if ( extract == NULL )
54 fprintf ( stream , " ERRORE DI ESTRAZIONE \ n " ) ;
else
56 {
i_print ( extract , stream ) ;
58 fprintf ( stream , " \ n " ) ;
i_free ( extract ) ;
60 }
}
62 void clean_queue ( Queue * queue )
{
64 if ( queue == NULL )
return ;
66 Item * extracted ;
while (( extracted = dequeue ( queue ) ) != NULL )
68 i_free ( extracted ) ;
}
70 void delete_queue ( Queue * queue )
{
72 if ( queue == NULL )
return ;
74 clean_queue ( queue ) ;
free ( queue ) ;
76 }
Listing 10.5: Queue (queue.c)
Queue * res ;
5 if (( res =( Queue *) malloc ( sizeof ( Queue ) ) ) == NULL )
return NULL ;
7 res - > head = res - > tail =0;
return res ;
9 }
108
11 {
return queue != NULL && queue - > tail == queue - > head ;
13 }
int is_full_queue ( Queue * queue )
15 {
if ( queue == NULL )
21 return 0;
if ( is_full_queue ( queue ) )
23 return 0;
queue - > item [ queue - > tail ] = add ;
25 queue - > tail = ( queue - > tail +1) % lenght_max_queue ;
return 1;
27 }
if ( is_empty_queue ( queue ) )
31 return NULL ;
Item * item ;
33 item = queue - > item [ queue - > head ];
queue - > head = ( queue - > head +1) % lenght_max_queue ;
35 return item ;
}
37 void print_dequeue ( Queue * queue , FILE * stream )
{
39 Item * extract ;
extract = dequeue ( queue ) ;
41 if ( extract == NULL )
fprintf ( stream , " ERRORE DI ESTRAZIONE \ n " ) ;
43 else
{
45 i_print ( extract , stream ) ;
fprintf ( stream , " \ n " ) ;
47 i_free ( extract ) ;
}
49 }
if ( queue == NULL )
53 return ;
Item * extracted ;
55 while (( extracted = dequeue ( queue ) ) != NULL )
i_free ( extracted ) ;
57 }
if ( queue == NULL )
61 return ;
clean_queue ( queue ) ;
63 free ( queue ) ;
}
Listing 10.7: Queue (queue.c)
109
Le operazione permesse devono essere quelle di:
• creazione di una nuova struttura (vuota).
• inserimento di un nuovo elemento della struttura.
• estrazione di un elemento dalla struttura.
• visualizzazione (a video) di tutti gli elementi nella base dati (opzionale).
• salvataggio della base dati su file (opzionale).
• caricamento di una nuova base dati da file (opzionale).
In questo esercizio, il programma deve essere realizzato su tre moduli distinti:
• l’interfaccia utente (il client).
• un modulo con le funzioni per la gestione della coda.
• un modulo con le funzioni per la gestione dei singoli dati.
In particolare, si desidera che l’implementazione della libreria sulla struttura dati corrisponda a un ADT
di I categoria.
Si supponga che ogni elemento della base dati possa essere (a scelta del programmatore):
• una stringa di lunghezza massima pari a 50 caratteri.
• un numero intero.
• una struttura composta da due campi (stringa + numero).
Si realizzi il programma in due versioni:
• supponendo che il numero massimo di elementi nella coda sia pari a 100 (implementazione tramite
vettori).
• supponendo che non ci sia limite al numero di elementi che è possibile memorizzare nella coda
(implementazione tramite liste).
# include < stdio .h >
2 # include < stdlib .h >
# include " stack . h "
4 # define pulisci system ( " cls " )
# define pausa while ( getchar () != ’\ n ’) ; getchar ()
6 void print_adt_stream ( Stack * pila , FILE * stream ) ;
int menu () ;
8 int read_adt_stream ( Stack * pila , FILE * stream ) ;
int main ()
10 {
110
28 break ;
case 2:
30 if (( item = i_read_by_stream ( stdin ) ) == NULL )
errori += printf ( " Errore nell ’ allocazione della
memoria .\ n " ) ;
32 if (! push ( pila , item ) )
errori += printf ( " Errore nell ’ inserimento dell ’ item .\ n
");
34 break ;
case 3:
36 if (( item = pop ( pila ) ) == NULL )
errori += printf ( " Errore nell ’ estrazione dell ’ item .\ n "
);
38 else
{
40 errori =1;
printf ( " Valore estratto : " ) ;
42 i_print ( item , stdout ) ;
printf ( " \ n " ) ;
44 i_free ( item ) ;
}
46 break ;
case 4:
48 errori =1;
print_adt_stream ( pila , stdout ) ;
50 break ;
case 5:
52 printf ( " Introduci il percorso nel quale salvare la
struttura dati :\ t " ) ;
scanf ( " % s " , path ) ;
54 if (( f = fopen ( path , " w " ) ) != NULL )
{
56 print_adt_stream ( pila , f ) ;
fflush ( f ) ;
58 fclose ( f ) ;
}
60 break ;
case 6:
62 printf ( " Introduci il percorso dal quale leggere la
struttura dati :\ t " ) ;
scanf ( " % s " , path ) ;
64 if (( f = fopen ( path , " r " ) ) != NULL )
{
66 if ( is_empty_stack ( pila ) )
{
68 delete_stack ( pila ) ;
if (( pila = new_stack () ) == NULL )
70 printf ( " Errore nell ’ allocazione della
memoria .\ n " ) ;
}
72 if (! read_adt_stream ( pila , f ) )
{
74 errori = 1;
printf ( " Errore nella lettura dell ’ ADT dal file \ n
");
76 }
fclose ( f ) ;
78 }
break ;
80 case 7:
delete_stack ( pila ) ;
82 fine =1;
111
}
84 if ( errori )
pausa ;
86 }
return 0;
88 }
int read_adt_stream ( Stack * pila , FILE * stream )
90 {
Item * item ;
92 while ( fgetc ( stream ) != ’\ n ’)
;
94 while (( item = i_read_by_stream ( stream ) ) != NULL )
if (! push ( pila , item ) )
96 return 0;
return 1;
98 }
Item * item ;
102 if ( is_empty_stack ( pila ) )
fprintf ( stream , " Struttura vuota .\ n " ) ;
104 else
{
106 fprintf ( stream , " Valori estratti :\ n " ) ;
while (( item = pop ( pila ) ) != NULL )
108 {
fprintf ( stream , " \ t " ) ;
110 i_print ( item , stream ) ;
fprintf ( stream , " \ n " ) ;
112 i_free ( item ) ;
}
114 }
}
116 int menu ()
{
118 int choose ;
pulisci ;
120 do
{
122 printf ( " \ nSeleziona l ’ operazione desiderata :\ n \ t1 - Creazione
nuovo Stack \ n \ t2 - Inserimento di un nuovo elemento \ n \ t3 -
Estrazione e stampa di un elemento \ n \ t4 - Visualizzazione a
video di tutti gli elementi \ n \ t5 - Salvataggio su file di
tutti gli elementi \ n \ t6 - Caricamento degli elementi da file \
n \ t7 - Esci \ nIntroduci la scelta desiderata :\ t " ) ;
scanf ( " % d " , & choose ) ;
124 if ( choose <1 || choose >7)
{
126 pulisci ;
printf ( " \ tERRORE IMMISSIONE DATI ! " ) ;
128 }
}
130 while ( choose <1 || choose >7) ;
return choose ;
132 }
Listing 10.8: Stack (client.c)
1 # include < stdio .h >
# include < malloc .h >
3 typedef int Item ;
void i_print ( Item * item , FILE * stream ) ;
112
5 void i_free ( Item * item ) ;
Item * i_read_by_stream ( FILE * stream ) ;
Listing 10.9: Stack (item.h)
1 # include " item . h "
void i_print ( Item * item , FILE * stream )
3 {
if ( item == NULL )
9 return ;
free ( item ) ;
11 }
Item * res ;
15 if (( res =( Item *) malloc ( sizeof ( Item ) ) ) == NULL || stream == NULL )
return NULL ;
17 if ( stream == stdin )
printf ( " Introduci un numero intero :\ t " ) ;
19 if ( fscanf ( stream , " % d " , res ) !=1)
{
21 free ( res ) ;
return NULL ;
23 }
return res ;
25 }
Listing 10.10: Stack (item.c)
Per la versione con allocazione dinamica saranno necessari questi altri due listati
1 # include < stdio .h >
# include < malloc .h >
3 # include " item . h "
struct stack_item_t
5 {
Item * item ;
7 struct stack_item_t * next ;
};
9 typedef struct Stack_t
{
11 struct stack_item_t * head ;
} Stack ;
13 Stack * new_stack () ;
int is_empty_stack ( Stack * queue ) ;
15 int push ( Stack * pila , Item * add ) ;
Item * pop ( Stack * pila ) ;
17 void print_dequeue ( Stack * pila , FILE * stream ) ;
void clean_stack ( Stack * pila ) ;
19 void delete_stack ( Stack * pila ) ;
Listing 10.11: Stack (stack.h)
1 # include < stdio .h >
# include " stack . h "
3 Stack * new_stack ()
{
5 Stack * res ;
if (( res = ( Stack *) malloc ( sizeof ( Stack ) ) ) == NULL )
113
7 return NULL ;
res - > head = NULL ;
9 return res ;
}
11 int is_empty_stack ( Stack * queue )
{
13 return queue == NULL || queue - > head == NULL ;
}
15 int push ( Stack * pila , Item * add )
{
17 if ( pila == NULL )
return 0;
19 struct stack_item_t * to_add ;
if (( to_add =( struct stack_item_t *) malloc ( sizeof ( struct stack_item_t )
) ) == NULL )
21 return 0;
to_add - > item = add ;
23 to_add - > next = pila - > head ;
pila - > head = to_add ;
25 return 1;
}
27 Item * pop ( Stack * pila )
{
29 if ( is_empty_stack ( pila ) )
return NULL ;
31 struct stack_item_t * head ;
Item * item ;
33 head = pila - > head ;
pila - > head = pila - > head - > next ;
35 item = head - > item ;
free ( head ) ;
37 return item ;
}
39 void print_pop ( Stack * pila , FILE * stream )
{
41 Item * extract ;
extract = pop ( pila ) ;
43 if ( extract == NULL )
fprintf ( stream , " ERRORE DI ESTRAZIONE \ n " ) ;
45 else
{
47 i_print ( extract , stream ) ;
fprintf ( stream , " \ n " ) ;
49 i_free ( extract ) ;
}
51 }
void clean_stack ( Stack * pila )
53 {
if ( pila == NULL )
55 return ;
Item * extracted ;
57 while (( extracted = pop ( pila ) ) != NULL )
i_free ( extracted ) ;
59 }
void delete_stack ( Stack * pila )
61 {
if ( pila == NULL )
63 return ;
clean_stack ( pila ) ;
65 free ( pila ) ;
}
114
Listing 10.12: Stack (stack.c)
if ( is_empty_stack ( pila ) )
29 return NULL ;
Item * item ;
31 item = pila - > item [ - - pila - > head ];
return item ;
33 }
Item * extract ;
37 extract = pop ( pila ) ;
if ( extract == NULL )
115
39 fprintf ( stream , " ERRORE DI ESTRAZIONE \ n " ) ;
else
41 {
i_print ( extract , stream ) ;
43 fprintf ( stream , " \ n " ) ;
i_free ( extract ) ;
45 }
}
47 void clean_stack ( Stack * pila )
{
49 if ( pila == NULL )
return ;
51 Item * extracted ;
while (( extracted = pop ( pila ) ) != NULL )
53 i_free ( extracted ) ;
}
55 void delete_stack ( Stack * pila )
{
57 if ( pila == NULL )
return ;
59 clean_stack ( pila ) ;
free ( pila ) ;
61 }
Listing 10.14: Stack (stack.c)
116
Capitolo 11
Esercitazione 8
• Se, nel momento in cui viene lanciato un job, è presente almeno un computer libero, il job viene
immediatamente eseguito su di esso.
• Se non è disponibile alcun calcolatore per eseguire un job, il job stesso viene temporaneamente
“salvato” in una lista d’attesa.
• Nel momento in cui un job termina la sua esecuzione su un pc, il gestore viene “avvertito” e un
altro job (se presente nella lista d’attesa) viene selezionato e lanciato su di esso. La selezione del
nuovo job da eseguire avviene secondo un criterio di massima priorità.
Si desidera implementare un semplice programma che emuli il comportamento del gestore dei job.
In particolare, il sistema deve essere realizzato su tre moduli:
• un modulo per la gestione dei singoli job: ciascun job è in realtà caratterizzato da numerose infor-
mazioni, ma per semplicità si supponga che, oltre al livello di priorità (intero positivo), ad esso sia
associata solo una stringa univoca di lunghezza massima pari a 30 caratteri.
• un modulo per la gestione della lista d’attesa: tale struttura dati, di fatto corrispondente a una
coda prioritaria, sia implementata (come quasi ADT o ADT di I categoria) mediante:
• un programma principale (client) che si occupa dell’interfaccia del gestore verso il mondo esterno,
al cui interno sono realizzate le due funzioni con cui il gestore può essere richiamato (ovvero, la
richiesta di esecuzione di un nuovo job da parte di un progettista e la segnalazione di terminazione
di un job che occupava uno dei computer). Per semplicità, si includa in tale modulo anche un main
che richiami tali funzioni a scelta dell’utente, richiedendo all’utente stesso le altre informazioni che
sono necessarie per attivare la funzione desiderata (per esempio, se si richiede di eseguire un nuovo
job, il suo “nome” e la sua priorità).
Il programma, infine, visualizzi sul video opportune informazioni a fronte di ogni richiesta ricevuta (per
esempio, che la richiesta di esecuzione un nuovo job è stata acquisita ma il job è stato salvato nella lista
d’attesa, oppure che un determinato job è stato estratto dalla lista d’attesa e lanciato su un calcolatore
che si è reso disponibile, etc.).
Il numero totale di computer dedicati che sono disponibili sia una costante predefinita, dichiarata nel
modulo principale.
1 # include < stdio .h >
# include < string .h >
117
3 # include " PriorityQueue . h "
# define n_computer 3
5 # define ma x_ co mm an d_ le ng ht 256
# define log_file " log . txt "
7 int main ()
{
9 char cmd [ m ax _c om ma nd _l en gh t +1] , name [30+1];
int fine =0 , i ;
11 FILE * flog ;
job * computer [ n_computer ]={0} , * tmp ;
13 Priority_Queue * job_list ;
if (( job_list = q_init () ) == NULL )
15 return 1;
if (( flog = fopen ( log_file , " w " ) ) == NULL )
17 return 2;
while (! fine )
19 {
printf ( " >> " ) ;
21 fgets ( cmd , max_command_lenght , stdin ) ;
if ( strstr ( cmd , " exit " ) == cmd )
23 fine =1;
else if ( strstr ( cmd , " job " ) == cmd )
25 {
if (( tmp = j_read_by_string ( cmd +4) ) == NULL )
27 printf ( " Errore lettura job . " ) ;
else if (! q_enqueue ( job_list , tmp ) )
29 printf ( " Errore inserimento job . " ) ;
else
31 {
printf ( " Job % s ( p =% d ) acquisito . " , j_getName ( tmp ) ,
j_getPriority ( tmp ) ) ;
33 fprintf ( flog , " Job % s ( p =% d ) acquisito . " , j_getName ( tmp
) , j_getPriority ( tmp ) ) ;
for ( i =0; i < n_computer && computer [ i ]!= NULL ; i ++)
35 ;
if ( i != n_computer )
37 {
printf ( " Job lanciato su pc % d . " , i ) ;
39 fprintf ( flog , " Job lanciato su pc % d . " , i ) ;
computer [ i ] = q_dequeue ( job_list ) ;
41 }
else
43 {
printf ( " Job messo in attesa . " ) ;
45 fprintf ( flog , " Job messo in attesa . " ) ;
}
47 }
printf ( " \ n " ) ;
49 fprintf ( flog , " \ n " ) ;
}
51 else if ( strstr ( cmd , " stop " ) == cmd )
{
53 sscanf ( cmd +4 , " % s " , name ) ;
for ( i =0; i < n_computer ; i ++)
55 if ( computer [ i ]!= NULL && strcmp ( name , j_getName ( computer [
i ]) ) ==0)
break ;
57 if ( i == n_computer )
printf ( " Processo non trovato in esecuzione . " ) ;
59 else
{
118
61 printf ( " Job % s ( p =% d ) terminato . " , j_getName (
computer [ i ]) , j_getPriority ( computer [ i ]) ) ;
fprintf ( flog , " Job % s ( p =% d ) terminato . " , j_getName (
computer [ i ]) , j_getPriority ( computer [ i ]) ) ;
63 j_free ( computer [ i ]) ;
computer [ i ] = q_dequeue ( job_list ) ;
65 if ( computer [ i ]!= NULL )
{
67 printf ( " Job % s ( p =% d ) lanciato su pc % d . " ,
j_getName ( computer [ i ]) , j_getPriority ( computer [ i
]) , i ) ;
fprintf ( flog , " Job % s ( p =% d ) lanciato su pc % d . " ,
j_getName ( computer [ i ]) , j_getPriority ( computer [ i
]) , i ) ;
69 }
}
71 printf ( " \ n " ) ;
fprintf ( flog , " \ n " ) ;
73 }
else
75 printf ( " Comando non riconosciuto . ( job , stop , exit ) \ n " ) ;
}
77 fflush ( flog ) ;
fclose ( flog ) ;
79 q_free ( job_list ) ;
return 0;
81 }
Listing 11.1: Coda prioritaria - I (client.c)
1 # include < stdio .h >
# include < malloc .h >
3 # include < string .h >
typedef unsigned int Priority ;
5 typedef struct
{
7 Priority priority ;
char * name ;
9 } job ;
job * j_init ( Priority priority , char * name ) ;
11 void j_print ( job * JOB , FILE * stream ) ;
void j_free ( job * JOB ) ;
13 job * j_read_by_stream ( FILE * stream ) ;
job * j_read_by_string ( char * string ) ;
15 char * j_getName ( job * JOB ) ;
int j_getPriority ( job * JOB ) ;
Listing 11.2: Coda prioritaria - I (job.h)
1 # include " job . h "
# define max_name_lenght 30
3 job * j_init ( Priority priority , char * name )
{
5 job * res ;
if ( priority <=0 || name == NULL )
7 return NULL ;
if (( res =( job *) malloc ( sizeof ( job ) ) ) == NULL )
9 return NULL ;
if (( res - > name = strdup ( name ) ) == NULL )
11 {
free ( res ) ;
13 return NULL ;
119
}
15 res - > priority = priority ;
return res ;
17 }
void j_print ( job * JOB , FILE * stream )
19 {
if ( JOB == NULL )
27 return ;
if ( JOB - > name == NULL )
29 free ( JOB - > name ) ;
free ( JOB ) ;
31 }
if ( stream == NULL )
35 return NULL ;
char name_tmp [ max_name_lenght +1];
37 Priority priority_tmp ;
if ( stream == stdin )
39 printf ( " Introduci : nome priorita ’\ n " ) ;
if ( fscanf ( stream , " % s % d " , name_tmp , & priority_tmp ) ==2)
41 return j_init ( priority_tmp , name_tmp ) ;
else
43 return NULL ;
}
45 job * j_read_by_string ( char * string )
{
47 if ( string == NULL )
return NULL ;
49 char name_tmp [ max_name_lenght +1];
Priority priority_tmp ;
51 if ( sscanf ( string , " % s % d " , name_tmp , & priority_tmp ) ==2)
return j_init ( priority_tmp , name_tmp ) ;
53 else
return NULL ;
55 }
if ( JOB == NULL )
59 return NULL ;
return JOB - > name ;
61 }
if ( JOB == NULL )
65 return -1;
return JOB - > priority ;
67 }
Listing 11.3: Coda prioritaria - I (job.c)
# include " job . h "
2 typedef job ITEM ;
int i_compare ( ITEM * I1 , ITEM * I2 ) ;
4 char * i_getDescription ( ITEM * I ) ;
void i_free ( ITEM * I ) ;
120
Listing 11.4: Coda prioritaria - I (ITEM.h)
1 # include " ITEM . h "
int i_compare ( ITEM * I1 , ITEM * I2 )
3 {
if ( I1 == NULL || I2 == NULL )
5 return 0;
return I1 - > priority - I2 - > priority ;
7 }
return j_getName ( I ) ;
11 }
j_free ( I ) ;
15 }
Listing 11.5: Coda prioritaria - I (ITEM.c)
Per la versione con heap saranno necessari questi altri due listati
# include < stdio .h >
2 # include < malloc .h >
# include " ITEM . h "
4 # define max_heap_dim 127
# define LEFT ( i ) 2* i +1
6 # define RIGHT ( i ) 2* i +2
# define PARENT ( i ) ( int ) (i -1) /2
8 typedef struct
{
10 ITEM ** value ;
int heapsize , max_heapsize ;
12 } heap ;
heap * h_init ( int size ) ;
14 int h_insert ( heap * HEAP , ITEM * V ) ;
void h_heapify ( heap * HEAP , int root ) ;
16 ITEM * h_remove ( heap * HEAP ) ;
void h_free ( heap * HEAP ) ;
Listing 11.6: Coda prioritaria - I (HEAP.h)
1 # include " HEAP . h "
heap * h_init ( int size )
3 {
if ( size <=0)
5 return NULL ;
heap * res ;
7 if (( res =( heap *) malloc ( sizeof ( heap ) ) ) == NULL )
return NULL ;
9 int i ;
i =1;
11 while (i <= size && i < max_heap_dim )
i < <=1;
13 i - -;
if (( res - > value =( ITEM **) malloc ( i * sizeof ( ITEM *) ) ) == NULL )
15 {
free ( res ) ;
17 return NULL ;
}
19 res - > heapsize =0;
121
res - > max_heapsize = i ;
21 return res ;
}
23 int h_insert ( heap * HEAP , ITEM * V )
{
25 if ( HEAP == NULL || V == NULL || HEAP - > heapsize >= HEAP - > max_heapsize )
return 0;
27 int position = HEAP - > heapsize ;
while ( position !=0 && i_compare (V , HEAP - > value [ PARENT ( position ) ]) >0)
29 {
HEAP - > value [ position ] = HEAP - > value [ PARENT ( position ) ];
31 position = PARENT ( position ) ;
}
33 HEAP - > value [ position ] = V ;
HEAP - > heapsize ++;
35 return 1;
}
37 void h_print ( heap * HEAP )
{
39 if ( HEAP == NULL )
return ;
41 int i ;
int l =1;
43 for ( i =0; i < HEAP - > heapsize ; i ++)
{
45 printf ( " % s " , i_getDescription ( HEAP - > value [ i ]) ) ;
if ( i ==(1 < < l ) -2)
47 {
printf ( " \ n " ) ;
49 l ++;
}
51 }
printf ( " \ n " ) ;
53 }
void h_heapify ( heap * HEAP , int root )
55 {
ITEM * t ;
57 if ( HEAP == NULL )
return ;
59 int largest = root ;
if ( LEFT ( root ) < HEAP - > heapsize && i_compare ( HEAP - > value [ LEFT ( root ) ] ,
HEAP - > value [ root ]) >0)
61 largest = LEFT ( root ) ;
if ( RIGHT ( root ) < HEAP - > heapsize && i_compare ( HEAP - > value [ RIGHT ( root ) ] ,
HEAP - > value [ largest ]) >0)
63 largest = RIGHT ( root ) ;
if ( largest != root )
65 {
t = HEAP - > value [ root ];
67 HEAP - > value [ root ] = HEAP - > value [ largest ];
HEAP - > value [ largest ] = t ;
69 h_heapify ( HEAP , largest ) ;
}
71 }
ITEM * h_remove ( heap * HEAP )
73 {
if ( HEAP == NULL || HEAP - > heapsize ==0)
75 return NULL ;
ITEM * VAL = HEAP - > value [0];
77 HEAP - > value [0] = HEAP - > value [ - - HEAP - > heapsize ];
HEAP - > value [ HEAP - > heapsize ] = NULL ;
79 h_heapify ( HEAP , 0) ;
122
return VAL ;
81 }
void h_free ( heap * HEAP )
83 {
if ( HEAP == NULL )
85 return ;
int i ;
87 for ( i =0; i < HEAP - > heapsize ; i ++)
i_free ( HEAP - > value [ i ]) ;
89 free ( HEAP - > value ) ;
free ( HEAP ) ;
91 }
Listing 11.7: Coda prioritaria - I (HEAP.c)
# include " HEAP . h "
2 typedef heap Priority_Queue ;
Priority_Queue * q_init () ;
4 int q_enqueue ( Priority_Queue * Queue , ITEM * I ) ;
ITEM * q_dequeue ( Priority_Queue * Queue ) ;
6 int q_is_empty ( Priority_Queue * Queue ) ;
void q_free ( Priority_Queue * Queue ) ;
Listing 11.8: Coda prioritaria - I (PriorityQueue.h)
1 # include " Priority_queue . h "
Priority_Queue * q_init ()
3 {
Priority_Queue * res ;
5 if (( res = h_init ( max_heap_dim ) ) == NULL )
return NULL ;
7 return res ;
}
9 int q_enqueue ( Priority_Queue * Queue , ITEM * I )
{
11 if ( Queue == NULL || I == NULL )
return 0;
13 return h_insert ( Queue , I ) ;
}
15 ITEM * q_dequeue ( Priority_Queue * Queue )
{
17 return h_remove ( Queue ) ;
}
19 int q_is_empty ( Priority_Queue * Queue )
{
21 if ( Queue == NULL )
return 1;
23 return Queue - > heapsize ==0;
}
25 void q_free ( Priority_Queue * Queue )
{
27 h_free ( Queue ) ;
}
Listing 11.9: Coda prioritaria - I (PriorityQueue.c)
mentre per la versione con linked-list ordinata sono necessari questi file
1 # include < malloc .h >
# include " ITEM . h "
3 struct node
{
5 ITEM * item ;
123
struct node * next ;
7 };
typedef struct
9 {
LIST * res ;
5 if (( res =( LIST *) malloc ( sizeof ( LIST ) ) ) == NULL )
return NULL ;
7 res - > head = NULL ;
return res ;
9 }
if ( List == NULL )
13 return ;
struct node * NODE , * extract ;
15 NODE = List - > head ;
while ( NODE != NULL )
17 {
extract = NODE ;
19 NODE = NODE - > next ;
free ( extract ) ;
21 }
free ( List ) ;
23 }
124
}
49 else
{
51 to_add - > next = succ ;
pred - > next = to_add ;
53 }
}
55 else
{
57 if ( pred == NULL )
List - > head = to_add ;
59 else
pred - > next = to_add ;
61 }
return 1;
63 }
ITEM * l_extract_max ( LIST * List )
65 {
if ( List == NULL )
79 return 1;
return List - > head == NULL ;
81 }
125
return l_init () ;
5 }
int q_enqueue ( Priority_Queue * Queue , ITEM * I )
7 {
l_free ( Queue ) ;
23 }
Listing 11.13: Coda prioritaria - I (PriorityQueue.c)
• ogni job sia caratterizzato, oltre che dal nome e dalla priorità, anche da un tempo di arrivo (ovvero,
l’orario in cui ne è stata richiesta l’esecuzione) e da una durata. Entrambi siano espressi nel formato
hh:mm:ss.
• l’insieme di tutti i job da eseguire, in numero ignoto, sia memorizzato in un file di testo, in ragione
di un job per riga del file, con il formato seguente:
Si assuma che i job contenuti in tale file siano ordinati per tempi di arrivo crescenti.
• l’operazione che il gestore dei job deve eseguire non sia di volta in volta richiesta all’utente: piuttosto,
a partire dal contenuto del file suddetto, il client dell’applicazione deduca automaticamente quando
i computer sono disponibili e l’istante di tempo in cui ogni job viene effettivamente lanciato su uno
dei calcolatori.
Suggerimento: Il client dell’applicazione, a partire dal contenuto del file dei job, simuli il comporta-
mento del gestore seguendo un ordine cronologico: ogni qual volta un nuovo job viene lanciato su un
computer, infatti, l’applicazione conosce il momento in cui il calcolatore sarà di nuovo disponibile. In
ogni istante, pertanto il gestore sa quanti sono i calcolatori liberi e quando ogni job che è stato lancia-
to sarà terminato. Processando un job per volta dal file di ingresso, quindi, è possibile capire qual è
l’avvenimento che accade per primo tra la presa in carico del nuovo job e un mutamento nello stato dei
calcolatori e/o dei job in lista d’attesa . . . .
La variazione rispetto all’esercizio 1 consiste solo nel client e nei campi della struttura job; la gestione
della coda prioritaria resta invariata.
# include < stdio .h >
2 # include < stdlib .h >
# include " Priority_queue . h "
4 # define jobList " joblist . txt "
# define n_computer 3
6 # define log_file " log . txt "
126
int execute_job ( job * computer [] , HOUR * h our_to _term inate [] , int *
pc_usati , int * cpu_to_terminate , job * to_add , HOUR * actual_moment ) ;
8 HOUR * terminate_job ( job * computer [] , HOUR * ho ur_to_ termin ate [] , int *
pc_usati , int * cpu_to_terminate ) ;
int main ()
10 {
job * tmp , * tmp_extract , * computer [ n_computer ]={0};
12 FILE * fin , * flog ;
Priority_Queue * job_list ;
14 HOUR * ho ur_to_ termin ate [ n_computer ]={0} , * h_tmp = NULL ;
int cpu_to_terminate = -1 , pc_usati =0;
16 if (( fin = fopen ( jobList , " r " ) ) == NULL || ( flog = fopen ( log_file , " w " ) ) ==
NULL )
return 1;
18 if (( job_list = q_init () ) == NULL )
return 2;
20 while (( tmp = j_read_by_stream ( fin ) ) != NULL )
{
22 if (! q_enqueue ( job_list , tmp ) )
{
24 printf ( " ERRORE inserimento job .\ n " ) ;
continue ;
26 }
if ( pc_usati !=0)
28 {
while ( H_compare ( tmp - > request , hour_ to_ter minate [
cpu_to_terminate ]) >0)
30 {
H_print ( ho ur_to_ termin ate [ cpu_to_terminate ] , stdout ) ;
32 printf ( " > Job % s ( p =% d ) terminato . " , j_getName (
computer [ cpu_to_terminate ]) , j_getPriority ( computer [
cpu_to_terminate ]) ) ;
H_print ( ho ur_to_ termin ate [ cpu_to_terminate ] , flog ) ;
34 fprintf ( flog , " > Job % s ( p =% d ) terminato . " , j_getName (
computer [ cpu_to_terminate ]) , j_getPriority ( computer [
cpu_to_terminate ]) ) ;
h_tmp = terminate_job ( computer , hour_to_terminate , &
pc_usati , & cpu_to_terminate ) ;
36 }
}
38 if ( pc_usati == n_computer )
{
40 H_print ( tmp - > request , stdout ) ;
printf ( " > Job % s ( p =% d ) acquisito . Job messo in attesa .\ n " ,
j_getName ( tmp ) , j_getPriority ( tmp ) ) ;
42 H_print ( tmp - > request , flog ) ;
fprintf ( flog , " > Job % s ( p =% d ) acquisito . Job messo in
attesa .\ n " , j_getName ( tmp ) , j_getPriority ( tmp ) ) ;
44 }
else
46 {
tmp_extract = q_dequeue ( job_list ) ;
48 if ( tmp == tmp_extract )
{
50 H_print ( tmp - > request , stdout ) ;
printf ( " > Job % s ( p =% d ) acquisito . " , j_getName ( tmp ) ,
j_getPriority ( tmp ) ) ;
52 H_print ( tmp - > request , flog ) ;
fprintf ( flog , " > Job % s ( p =% d ) acquisito . " , j_getName (
tmp ) , j_getPriority ( tmp ) ) ;
54 }
printf ( " Job % s ( p =% d ) lanciato su pc .\ n " , j_getName (
127
tmp_extract ) , j_getPriority ( tmp_extract ) ) ;
56 fprintf ( flog , " Job % s ( p =% d ) lanciato su pc .\ n " , j_getName (
tmp_extract ) , j_getPriority ( tmp_extract ) ) ;
if (! execute_job ( computer , hour_to_terminate , & pc_usati , &
cpu_to_terminate , tmp_extract , h_tmp != NULL ? h_tmp :
tmp_extract - > request ) )
58 {
printf ( " ERRORE esecuzuine job .\ n " ) ;
60 continue ;
}
62 if ( tmp != tmp_extract )
{
64 H_print ( tmp - > request , stdout ) ;
printf ( " > Job % s ( p =% d ) acquisito . Job messo in attesa
.\ n " , j_getName ( tmp ) , j_getPriority ( tmp ) ) ;
66 H_print ( tmp - > request , flog ) ;
fprintf ( flog , " > Job % s ( p =% d ) acquisito . Job messo in
attesa .\ n " , j_getName ( tmp ) , j_getPriority ( tmp ) ) ;
68 }
}
70 }
while ( pc_usati !=0)
72 {
H_print ( ho ur_to_ termin ate [ cpu_to_terminate ] , stdout ) ;
74 printf ( " > Job % s ( p =% d ) terminato . " , j_getName ( computer [
cpu_to_terminate ]) , j_getPriority ( computer [ cpu_to_terminate ])
);
H_print ( ho ur_to_ termin ate [ cpu_to_terminate ] , flog ) ;
76 fprintf ( flog , " > Job % s ( p =% d ) terminato . " , j_getName ( computer
[ cpu_to_terminate ]) , j_getPriority ( computer [ cpu_to_terminate
]) ) ;
h_tmp = terminate_job ( computer , hour_to_terminate , & pc_usati , &
cpu_to_terminate ) ;
78 if (( tmp_extract = q_dequeue ( job_list ) ) != NULL )
{
80 printf ( " Job % s ( p =% d ) lanciato su pc . " , j_getName (
tmp_extract ) , j_getPriority ( tmp_extract ) ) ;
fprintf ( flog , " Job % s ( p =% d ) lanciato su pc . " , j_getName (
tmp_extract ) , j_getPriority ( tmp_extract ) ) ;
82 if (! execute_job ( computer , hour_to_terminate , & pc_usati , &
cpu_to_terminate , tmp_extract , h_tmp != NULL ? h_tmp :
tmp_extract - > request ) )
{
84 printf ( " ERRORE esecuzuine job .\ n " ) ;
continue ;
86 }
}
88 printf ( " \ n " ) ;
fprintf ( flog , " \ n " ) ;
90 }
j_free ( tmp ) ;
92 j_free ( tmp_extract ) ;
q_free ( job_list ) ;
94 H_free ( h_tmp ) ;
fclose ( fin ) ;
96 fflush ( flog ) ;
fclose ( flog ) ;
98 return 0;
}
100 HOUR * terminate_job ( job * computer [] , HOUR * ho ur_to_ termin ate [] , int *
pc_usati , int * cpu_to_terminate )
{
128
102 HOUR * res ;
j_free ( computer [* cpu_to_terminate ]) ;
104 computer [* cpu_to_terminate ] = NULL ;
res = h our_to _termi nate [* cpu_to_terminate ];
106 hou r_to_t ermina te [* cpu_to_terminate ] = NULL ;
(* pc_usati ) - -;
108 int i ;
* cpu_to_terminate = -1;
110 for ( i =0; i < n_computer ; i ++)
if ( computer [ i ]!= NULL && (* cpu_to_terminate == -1 || H_compare (
hou r_to_t ermina te [ i ] , hou r_to_t ermina te [* cpu_to_terminate ])
<0) )
112 * cpu_to_terminate = i ;
return res ;
114 }
int execute_job ( job * computer [] , HOUR * h our_to _term inate [] , int *
pc_usati , int * cpu_to_terminate , job * to_add , HOUR * actual_moment )
116 {
129
int H_compare ( HOUR * h1 , HOUR * h2 )
5 {
if ( h1 == NULL || h2 == NULL )
7 return 0;
return h1 - > S - h2 - > S ;;
9 }
HOUR * res ;
13 if (h <0 || h >=24 || m <0 || m >=60 || s <0 || s >=60)
return NULL ;
15 if (( res =( HOUR *) malloc ( sizeof ( HOUR ) ) ) == NULL )
return NULL ;
17 res - > S = h *3600+ m *60+ s ;
return res ;
19 }
if ( stream == NULL )
23 return NULL ;
int h , m , s ;
25 if ( fscanf ( stream , " % d :% d :% d " , &h , &m , & s ) ==4)
return H_init (h , m , s ) ;
27 else
return NULL ;
29 }
if ( H == NULL )
39 return ;
free ( H ) ;
41 }
if ( H == NULL )
45 return NULL ;
return H_init (H - > S /3600 , (H - > S /60) %60 , H - > S %60) ;
47 }
if ( H1 == NULL || H2 == NULL )
51 return 0;
H1 - > S += H2 - > S ;
53 return 1;
}
Listing 11.16: Coda prioritaria - II (Hour.c)
# include < stdio .h >
2 # include < malloc .h >
# include < string .h >
4 # include " Hour . h "
typedef unsigned int Priority ;
6 typedef struct
{
130
8 Priority priority ;
char * name ;
10 HOUR * request , * duration ;
} job ;
12 job * j_init ( Priority priority , char * name , HOUR * request , HOUR * duration
);
void j_print ( job * JOB , FILE * stream ) ;
14 void j_free ( job * JOB ) ;
job * j_read_by_stream ( FILE * stream ) ;
16 job * j_read_by_string ( char * string ) ;
char * j_getName ( job * JOB ) ;
18 int j_getPriority ( job * JOB ) ;
Listing 11.17: Coda prioritaria - II (job.h)
# include " job . h "
2 # define max_name_lenght 30
job * j_init ( Priority priority , char * name , HOUR * request , HOUR * duration
)
4 {
job * res ;
6 if ( priority <=0 || name == NULL || request == NULL || duration == NULL )
return NULL ;
8 if (( res =( job *) malloc ( sizeof ( job ) ) ) == NULL )
return NULL ;
10 if (( res - > name = strdup ( name ) ) == NULL )
{
12 free ( res ) ;
return NULL ;
14 }
res - > priority = priority ;
16 res - > request = request ;
res - > duration = duration ;
18 return res ;
}
20 void j_print ( job * JOB , FILE * stream )
{
22 if ( JOB == NULL || stream == NULL )
return ;
24 fprintf ( stream , " % s (% d ) " , JOB - > name , JOB - > priority ) ;
H_print ( JOB - > request , stream ) ;
26 fprintf ( stream , " " ) ;
H_print ( JOB - > duration , stream ) ;
28 }
if ( JOB == NULL )
32 return ;
if ( JOB - > name != NULL )
34 free ( JOB - > name ) ;
if ( JOB - > request != NULL )
36 H_free ( JOB - > request ) ;
if ( JOB - > duration != NULL )
38 H_free ( JOB - > duration ) ;
free ( JOB ) ;
40 }
if ( stream == NULL )
44 return NULL ;
char name_tmp [ max_name_lenght +1];
46 Priority priority_tmp ;
131
int hr , mr , sr , hd , md , sd ;
48 if ( stream == stdin )
printf ( " Introduci : nome priorita ’ ora_richiesta durata \ n " ) ;
50 if ( fscanf ( stream , " % s % d % d :% d :% d % d :% d :% d " , name_tmp , & priority_tmp
, & hr , & mr , & sr , & hd , & md , & sd ) ==8)
return j_init ( priority_tmp , name_tmp , H_init ( hr , mr , sr ) , H_init
( hd , md , sd ) ) ;
52 else
return NULL ;
54 }
job * j_read_by_string ( char * string )
56 {
if ( string == NULL )
58 return NULL ;
char name_tmp [ max_name_lenght +1];
60 Priority priority_tmp ;
int hr , mr , sr , hd , md , sd ;
62 if ( sscanf ( string , " % s % d % d :% d :% d % d :% d :% d " , name_tmp , & priority_tmp
, & hr , & mr , & sr , & hd , & md , & sd ) ==8)
return j_init ( priority_tmp , name_tmp , H_init ( hr , mr , sr ) , H_init
( hd , md , sd ) ) ;
64 else
return NULL ;
66 }
if ( JOB == NULL )
70 return NULL ;
return JOB - > name ;
72 }
if ( JOB == NULL )
76 return -1;
return JOB - > priority ;
78 }
Listing 11.18: Coda prioritaria - II (job.c)
132