Si consideri la seguente funzione:
def Exam(n):
tot = n
if n <= 1:
return tot
tot = tot + Exam(n − 1)
b = n − 1
j = n
while j >= 0:
tot = tot + j
j = j − 2
return tot + Exam(b)
a) Si imposti la relazione di ricorrenza che ne definisce il tempo di esecuzione giustificando dettagliatamente l’equazione ottenuta
Si rientrerà nel caso base per ogni
Per il caso generale, invece, il costo sarà determinato
-
dalle 2 chiamate ricorsive
-
dal ciclo while
-
ciclo while
Si analizza il suo comportamento
iterazione | 1 | 2 | 3 | ||
---|---|---|---|---|---|
il ciclo while terminerà quando
il costo sarà uguale a
-
chiamate ricorsive
Effettuate rispettivamente prima e dopo il ciclo while, esse presentato lo stesso parametro
$\Large n - 1$ , da cui il costo$\Large 2*T(n-1)$
b) Si risolva la ricorrenza usando il metodo di sostituzione e si dimostri così che la soluzione è
- si elimina preliminarmente la notazione asintotica mediante l'ulitizzo di variabili ausiliarie
riscrivendo
- si ipotizza, come richiesto dal testo, che la soluzione sia
$\Large T(n) = O(n * 2^n)$ , dunque bisognerà dimostrare che
con
- passo base
dunque per ogni
- passo induttivo
dunque per ogni
Sia dato un array A ordinato di n interi distinti ed un intero x;
si vuole trovare l’indice in A del più piccolo intero maggiore di x.
Progettare un algoritmo iterativo efficiente che risolva il problema.
Se l’array contiene solo elementi minori o uguali ad x, l’algoritmo deve restituire −1.
Ad esempio: per A = [1, 2, 8, 10, 11, 12, 19], assumendo che le posizioni dell’array partano da 0,
-
per
$x = 7$ l’algoritmo deve restituire 2 (cio è l’indice dell’elemento 8), -
per
$x = 30$ l’algoritmo deve restituire −1.
Dell’algoritmo proposto
a) si dia la descrizione a parole
Le 2 caratteristiche dell'array (ordinato e con tutti elementi distinti) portando inevitabilmente all'utilizzo della ricerca binaria per poter soddisfare la richiesta del testo in modo efficente.
Partendo dall'elemento centrale dell'array (usando un'indice ausiliario m)
-
se
$A[m] > x$ , l'elemento cercato (se presente) si troverà nel sub-array di sinistra -
altrimenti, bisognerà cercare nel sub-array di destra
si continuerà a ripetere il procedimento fino a quando il k-esimo sub-array (con $\Large k = log_2(n)$) conterrà un solo elemento, procedendo così all'ultimo controllo
-
se l’elemento è strettamente maggiore di x, viene restituita la posizione di quest'ultimo
-
altrimenti, viene restituito -1
b) si scriva lo pseudocodice
def es2(A, x):
s = 0
d = len(A) - 1
while s < d:
m = (s + d) / 2
if A[m] > x:
d = m
else:
l = m + 1
if A[s] > x:
return s
return -1
c) si giustifichi il costo computazionale
il costo dell'algoritmo è determinato dal ciclo while, si analizza il suo comportamento
iterazione | 0 | 1 | 2 | ||
---|---|---|---|---|---|
lunghezza sub-array |
il ciclo while terminerà quando
il costo dell'algoritmo sarà pari a
Si consideri un albero binario radicato T , i cui nodi hanno
-
un campo val contenente un intero
-
e i campi left e right con i puntatori ai figli.
Bisogna modificare il campo val di ciascun nodo in modo che il nuovo risulti la somma del valore originario incrementata dal valore originario degli eventuali figli.
Si consideri ad esempio l’albero T in figura a sinistra, a destra viene riportato il risultato della modifica di T .
5 13
/ \ / \
/ \ / \
6 2 6 9
/ \ / \
/ \ / \
4 3 9 8
/ / \ / / \
/ / \ / / \
5 1 4 5 1 4
Progettare un algoritmo ricorsivo che, dato il puntatore r alla radice di T memorizzato tramite record e puntatori, effettui l’operazione di modifica in tempo
Dell’algoritmo proposto
a) si dia la descrizione a parole
si visita l'albero in pre-order, sommando al valore del nodo corrente la somma dei valori dei suoi figli, se presenti
b) si scriva lo pseudocodice
def es3(r):
if r.left != None:
r.val += r.left.val
if r.right != None:
r.val+ = r.right.val
if r.left != None:
es3(r.left)
if r.right != None:
es3(r.right)
c) si giustifichi il costo computazionale
il costo è quello di una visita in pre-order, con equazione di ricorrenza
dove
-
$\Large n$ è il numero di nodi dell'albero -
$\Large k$ è il numero di nodi del sottoalbero sinistro -
e
$\Large n-k-1$ è il numero di nodi del sottoalbero destro
Per determinarne il costo, si analizzano caso migliore e caso peggiore:
-
caso peggiore:
l'albero è completamente sbilanciato, quindi tutti i suoi nodi sono aggregati o nel sottoalbero sinistro o nel sottoalbero destro, ovvero quando
•
•
sviluppando la ricorrenza, si ottiene
generalizzabile in
Verrà raggiunto il caso base quando
Il costo sarà uguale a
-
caso migliore:
l'albero è completo, quindi ogni nodo padre ha esattamente 2 figli, ovvero quando sia il sottoalbero sinistro che il sottoalbero destro presentano
$\Large \frac{n-1}{2}$ nodi, sostituendo i valori nell'equazione di ricorrenza si ottiene
Generalizzando la ricorrenza in
vengono individuati
Si ricade nel primo caso del metodo principale, poichè per
Si conclude che
Avendo trovato come caso peggiore
Viene dunque rispettato il vincolo per il quale il costo computazionale dell’algoritmo debba essere uguale a