Consideriamo il seguente codice C: #include <stdio.h> int z = 0 ; void sss (void) ; void ttt( void) ; main( ) { int z = 1 ; sss (); ttt (); } void ppp( void) ; void sss (void) { ppp(); } void ttt (void) { int z = 2; ppp (); } void ppp( void) { printf ("%d " , z ) ; }Domanda: qual' e` l'output ?
Risposta:
Giustificazione (NON richiesta): la dichiarazione che ha valore per la
procedura ppp e` quella a livello di file, cioe` la prima, int z = 0
(vedere dispense, la sezione sulle
regole di visibilita`).
Consideriamo il seguente codice C: #include <stdio.h> #include <stdlib.h> void sss(int * x); main() { int * p; int * q ; p = (int*) malloc (1*sizeof(int)); *p = 5; q = p ; sss(q); printf ("%d ", *p); } void sss(int * x) { *x = 3 ;}Domanda: qual'e` l'output ?
Risposta:
Giustificazione (NON richiesta): nel main, i due puntatori p e q
puntano alla stessa cella; l'indirizzo di tale cella viene passato alla
procedura sss che modifica il contenuto della cella stessa.
Domanda 1) :
(logaritmo in base 3 di (2 alla n)) appartiene a Theta(logaritmo in
base 2 di (3 alla n)) ??
Risposta: SI
Giustificazione (NON richiesta):
logaritmo in base 3 di (2 alla n) = n * (logaritmo in base 3 di 2)
= n * costante
logaritmo in base 2 di (3 alla n) = n * (logaritmo in base 2 di 3)
= n * costante'
Domanda 2) :
Se sappiamo che per ogni n maggiore di 3 :
g(n) ed f(n) sono maggiori (strettamente) di 0 ed inoltre il rapporto
f(n)/g(n) e` minore o uguale di 20
possiamo concludere che f e` in Theta(g) ??
Risposta: NO
Giustificazione (NON richiesta):
Basta considerare f e g tali che f(n) = 1 ed g(n) = n.
In effetti, tutto quello che si puo' concludere e` che f e` in O(g).
L'insieme X e` dato dalla seguente definizione (induttiva):
(base) L'insieme Lettere e` contenuto in X
(passo_1) se w, z appartengono a X allora w#z e w%z appartengono a X
(passo_2) se w appartiene a X allora (w) appartiene a X
L'insieme Y e` dato dalla seguente definizione (induttiva):
(base) L'insieme Lettere e` contenuto in Y
(passo) se w, z appartengono a Y allora (w#z) e w%z appartengono a Y
Domanda: quali delle seguenti affermazioni sono vere ?
(1) X e` contenuto in Y
(2) Y e` contenuto in X
(3) gli unici elementi a comune tra X e Y sono le lettere
e le espressioni formate
solo da lettere e da %
(4) nessuna delle precedenti
Sul foglio risposte indicare solo i numeri delle affermazioni che credete vere.
Risposta: L'unica vera e` la (2).
Giustificazione (NON richiesta):
poiche' la base e` uguale e la regola che, prese z e w, restituisce w%z e`
presente nei due passi,
basta osservare che
quello che si ha, per Y, usando la regola che
prese w e z restituisce (w#z),
in X
si ottiene in due passi combinando la regola che prese w e z restituisce
w#z, con quella che mette le parentesi attorno ad una qualunque
espressione.
Una dimostrazione formale si puo' fare per induzione.
h1 : ST ---> { 0, ..., 99 } data da:
h1( x1 x2 ... xn ) = ( cod(x1) cod(x2) ... cod(xn) ) mod 100
esempio h1(ABCD) = (01020304) mod 100 = 1020304 mod 100 = 4
(quindi, i codici si concatenano in modo da formare un numero decimale ...)
h2 : ST ---> { 0, ..., 99 } data da:
h2( x1 x2 ... xn ) = ( cod(x1) + cod(x2) + ... + cod(xn) ) mod 100
esempio h2(ABCD) = (01+02+03+04) mod 100 = 10 mod 100 = 10
Domanda: come funzioni di hashing, le due funzioni sono ugualmente valide, oppure una e` migliore dell'altra, e quale?? Giustificare brevemente la risposta (senza giustificazione, la risposta, anche giusta, non e` valida).
Risposta: La funzione h2 e` decisamente migliore. Infatti:
--- mod 100 = prendere il resto della divisione per 100, quindi considerare
solo le ultime 2 cifre decimali;
-- per questo motivo, la funzione h1 considera solo l'ultima lettera di
ogni stringa e quindi lascia inutilizzati circa 80 dei 100 buckets !!!!
--- la h2 invece utilizza tutti i buckets.
typedef struct nodo * lista; /* lista e` un puntatore a nodo */ struct nodo { char info; lista next;} ; typedef struct { lista first ; /* puntatore alla testa */ lista last ; /* puntatore alla coda */ } coda ;
Risposta 2.
Sul foglio risposte bastava fare un disegno; qui descrivo a parole.
La coda vuota si implementa con un recod di tipo coda che ha i due
puntatori nulli.
La coda formata da A, B, C si implementa con un record in cui il
puntatore first punta alla testa di una lista semplice, di 3 elementi
(cioe` record); il primo
contiene A, il secondo B, il terzo C; il puntatore last punta al record
che contiene C.
Risposta 3. (usando pseudo-codice)
procedura conc ( c1 : coda --- Parametro IN/OUT c2 : coda --- Parametro IN, ma anche IN/OUT ) { if c2 non e` vuota /* per questo test, basta controllare il puntatore first */ then { if c1 non e` vuota then { (c1.last ->next) <--- c2.first c1.last <--- c2.last } else c1 <--- c2 /* volendo ora si puo` modificare il valore di c2, MA SENZA LIBERARE MEMORIA */ } }
Pezzo di programma
(1) ham <-- p ; righe <-- 1 ; continua <-- true (2) while (righe < n) and continua do { (2.1) k <-- righe +1 (2.2) while ( k <= n ) and continua do { (2.2.1) aux <-- 0 (2.2.2) per j = 1, 2, ..., p : if cod[righe, j] e` diverso da cod[k, j] then aux <-- aux + 1 (2.2.3) if ham > aux then ham <-- aux (2.2.4) if ham = 0 then continua <-- false else k <-- k+1 } chiude il while (2.2) (2.3) righe <-- righe+1 } chiude il while (2) (3) scrivi( ham )
Domanda:
Determinare la complessita` del pezzo di programma, in funzione di n e di p ,
nel caso peggiore, possibilmente in Theta( ... ).
Non fare conti troppo dettagliati (esplicitando tutte le costanti,....), ma non limitarsi
nemmeno a dare il risultato, o a quattro chiacchiere; in particolare: precisare
se c'e` un caso
peggiore (o caso pessimo) e qual'e`.
Soluzione:
Scriviamo: x^y per indicare "x elevato a y"
La complessita` di tutto e` in Theta(p * n^2) . Infatti:
a(n - righe) p + b(n - righe) + cper righe = 1,2,,,,n-1
Alternativamente, si poteva ragionare con le operazioni dominanti (ed era piu' semplice):
un albero e` un puntatore a nodo; un nodo e` un record con 3 campi: info (di tipo
che non interessa), sin, des (di tipo albero).
La procedura che segue calcola, in modo poco furbo, l'altezza dell'albero; per comodita`, all'albero vuoto attribuiamo altezza -1.
Usiamo { } e -> come in C.
function alt ( t : albero binario) : integer { if t e` vuoto then return( -1 ) else if alt( t-> sin ) < alt( t->des ) then return( 1 + alt( t->des ) ) else return( 1 + alt( t->sin ) ) }
Domanda:
Determinare la complessita` di una generica chiamata
Non fare conti troppo dettagliati (esplicitando tutte le costanti,....), ma non limitarsi
nemmeno a dare il risultato, o a quattro chiacchiere; in particolare: precisare
se c'e` un
caso peggiore (o caso pessimo) e qual'e`.
Soluzione:
La complessita` della chiamata e` in Theta(3^h), dove (3^h) indica (3
elevato alla h). Infatti:
T(0) = ce risolverlo per sostituzioni successive.
T(h) = 3 T(h-1) + dcon c, d costanti