Salta al contenuto
Note per Studenti Problemi computazionali e algoritmi

Problemi computazionali e algoritmi

In questa pagina 6

Problema computazionale

Un problema computazionaleSpecifica di cosa si vuole ottenere, indipendente dal modo di ottenerlo. Π\Pi è un insieme di coppie (i,s)(i, s) con i∈Ii \in I (istanza, dominio delle istanze) e s∈Ss \in S (soluzione, dominio delle soluzioni). Si richiede che ogni istanza abbia almeno una soluzione: Π⊆I×S\Pi \subseteq I \times S.

Problema II SS Π\Pi
somma di interi coppie (x,y)(x, y) Z\mathbb{Z} ((x,y),s)((x,y), s) con s=x+ys = x + y
ordinamento (v. 1) array di interi AA array ordinati BB (A,B)(A, B) con BB permutazione ordinata di AA
ordinamento (v. 2) array di interi AA permutazioni PP (A,P)(A, P) con PP che ordina AA

Due osservazioni: una soluzione può servire a più istanze (la somma 1010 vale per (1,9)(1,9) e per (4,6)(4,6)) e un'istanza può avere più soluzioni (nella v. 2 con chiavi ripetute, più permutazioni ordinano lo stesso array). In quel caso l'algoritmo ne calcola una.

Algoritmo e modello di calcolo

Un algoritmo AA risolve Π\Pi se, ricevuta in input un'istanza i∈Ii \in I, produce in output una soluzione ss con (i,s)∈Π(i, s) \in \Pi: calcola una funzione che a ogni istanza associa una sua soluzione. I passi sono quelli elementari di un modello di calcolo.

Il modello usato è la macchina RAMRandom Access Machine: input, output, dati intermedi e programma stanno in memoria, e ogni accesso a una cella costa un passo. Non c'entra la memoria RAM del PC.: i passi elementari sono assegnamento, operazioni logiche e aritmetiche, indicizzazione di array, restituzione di un valore da un metodo; ciascuno costa Θ(1)\Theta(1).

Gli algoritmi si scrivono in pseudocodice: costrutti di un linguaggio ad alto livello (for, while, if, ricorsione) mescolati a linguaggio naturale, senza dettagli di tipi o sintassi. Negli esami si richiede sempre di specificare input e output dell'algoritmo prima del corpo.

Taglia di un'istanza

La taglia è una funzione che associa a ogni istanza uno o più numeri che ne misurano la grandezza: la lunghezza di un array, il numero di nodi di un albero, il numero di vertici e archi di un grafo (nn ed mm). La complessità si esprime in funzione della taglia (vedi Complessità in tempo e caso pessimoPerché lo studio sperimentale non basta; complessità al caso pessimo come massimo sul numero di operazioni tra le istanze di una data taglia; stima con limiti superiore e inferiore senza trovare l'istanza peggiore; esempi arrayMax, prefixAverages e InsertionSort; efficienza asintotica e limiti dell'analisi.Complessità in tempo e caso pessimo →).

Strutture dati e ADT

Un algoritmo organizza i dati in una struttura dati: collezione di oggetti con metodi per accedervi e modificarla. Due livelli:

  1. ADT (tipo di dato astratto): specifica il tipo dei dati, le operazioni ammesse e i loro parametri, cioè cosa fa ogni operazione e non come. Si descrive con un'interfaccia.
  2. Struttura dati concreta: l'implementazione dell'ADT (una classe in Java, un insieme di struct e funzioni in C), che fissa come agiscono le operazioni e quindi il loro costo. Vedi Liste, pile e codeRipasso degli ADT elementari: lista index-based (array) e position-based (lista doppiamente concatenata con sentinelle), pila LIFO, coda FIFO, deque, iteratori; interfacce, implementazioni e costi; array estendibile e coda circolare.Liste, pile e code →.

Esempio svolto: cercare in un array ordinato

Problema. II: coppie (AA array crescente di nn interi distinti, xx intero). SS: un indice ii con A[i]=xA[i] = x, oppure −1-1 se xx non c'è.

Ricerca lineare. Scorre tutto l'array: nel caso peggiore xx manca e si fanno nn confronti, Θ(n)\Theta(n).

Ricerca binaria. Si sfrutta l'ordine: si confronta xx con l'elemento centrale e si scarta metà dell'intervallo.

Algoritmo ricercaBinaria(A, n, x)
Input: array crescente A[0..n-1] di interi distinti, intero x
Output: indice di x in A, oppure -1
lo <- 0; hi <- n - 1
while lo <= hi do
    mid <- floor((lo + hi) / 2)
    if A[mid] = x then return mid
    if A[mid] < x then lo <- mid + 1
    else hi <- mid - 1
return -1
  • Invariante: se xx è in AA, allora la sua posizione sta in [lo,hi][lo, hi]. Vale all'inizio ([0,n−1][0, n-1] è tutto l'array) e si conserva: se A[mid]<xA[mid] < x, per l'ordine xx può stare solo a destra di midmid.
  • Complessità: l'intervallo dimezza a ogni iterazione, quindi ci sono al più ⌊log⁡2n⌋+1\lfloor \log_2 n \rfloor + 1 iterazioni di costo costante: O(log⁡n)O(\log n).

Traccia su A=[2,5,8,12,16,23,38]A = [2, 5, 8, 12, 16, 23, 38], x=23x = 23: lo=0,hi=6,mid=3lo=0, hi=6, mid=3 (12<2312<23) →lo=4\to lo=4; mid=5mid=5, A[5]=23A[5]=23, restituisce 55.

Errori comuni

  • Confondere problema e algoritmo: il problema dice cosa, l'algoritmo come; per lo stesso problema esistono più algoritmi.
  • Dimenticare di dichiarare input e output nello pseudocodice, o scegliere una taglia ambigua (per un grafo servono nn e mm).
  • Scrivere mid=(lo+hi)/2mid = (lo + hi)/2 senza la parte intera: gli indici devono essere interi.

Versione ripasso

Teoria collegata