Problemi computazionali - istanze, soluzioni, correttezza e complessità
In questa pagina 3
Il capitolo fissa il linguaggio con cui tutta la materia descrive un algoritmo: che cosa deve fare (il problema), che cosa fa (l'algoritmo), se lo fa bene (correttezza) e quanto costa (complessità). Sono richiami più formali di Problemi computazionali e algoritmiProblema computazionale come insieme di coppie (istanza, soluzione); algoritmo e modello di calcolo RAM; pseudocodice; taglia di un'istanza; ADT e struttura dati concreta; esempio svolto con ricerca lineare e binaria in un array ordinato.Problemi computazionali e algoritmi →, 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 → e Notazione asintoticaDefinizioni di O, Omega, Theta e o piccolo con le costanti c ed n0; esempi con costanti esplicite; proprietà (polinomi, esponenziali, logaritmi, somme, implicazioni tra notazioni); sommatorie notevoli; terminologia (logaritmica, lineare, polinomiale, esponenziale).Notazione asintotica → di Dati e Algoritmi, che si danno per noti.
Algoritmo e problema computazionale
Definizione (algoritmo). Un algoritmoProcedura finita e deterministica: sequenza di passi elementari di un modello di calcolo. è una procedura computazionale finita e deterministica, specificata come sequenza di passi elementari (istruzioni) di un dato modello computazionale, che trasforma un ingresso in una e una sola uscita. Dal punto di vista insiemistico un algoritmo è una funzione dall'insieme degli ingressi a quello delle uscite. La particolare funzione realizzata è descritta dalla sequenza finita di istruzioni.
Il modello computazionale è l'astrazione di un calcolatore e del suo insieme di istruzioni (nel corso: la RAM con pseudocodice, come in Dati e Algoritmi).
Definizione (problema computazionale). Un problema computazionale è una specifica astratta della relazione tra un insieme di istanze e un insieme di soluzioni : Si scrive anche per . La relazione deve essere:
- totale: ogni istanza ha almeno una soluzione, ;
- non necessariamente univoca: possono esistere con e .
Il problema è un oggetto logico (una relazione), indipendente dal modo di risolverlo. L'algoritmo ne è una funzione: quando l'istanza ha più soluzioni, l'algoritmo ne sceglie una sola.
Definizione (algoritmo che risolve un problema). Un algoritmo risolve se , e
Osservazione (codifica). Identificare ingressi e uscite dell'algoritmo con istanze e soluzioni astratte è una semplificazione: e sono in realtà codifiche di e compatibili con il modello di calcolo (per si può prendere l'insieme delle stringhe binarie). La distinzione si richiama solo quando serve, per esempio per la taglia di un intero (più sotto).
Esempi di problemi
Somma di due interi. , , . Qui la relazione è una funzione (ogni istanza ha una sola soluzione).
Raggiungibilità in un grafo diretto. Un grafo diretto è con finito ed . Un cammino è una sequenza , , con per ; esiste anche il cammino degenere , senza archi. Il problema chiede un cammino da a , se esiste:
- ;
- , dove è la sequenza vuota e significa "non esiste";
- contiene se è un cammino da a , e se nessun cammino esiste.
Qui la relazione non è univoca: tra e ci possono essere più cammini, e tutti sono soluzioni valide.
Ordinamento di sequenze di interi. La stella di Kleene di un insieme è l'insieme di tutte le sequenze finite, vuota compresa, di elementi di ; ha lunghezza . Allora e I vincoli (1) e (2) dicono che la soluzione è una sequenza ordinata della stessa lunghezza, il (3) che è una permutazione degli elementi dell'istanza. Qui contiene anche sequenze non ordinate: non è richiesto che ogni elemento di sia in relazione con qualche istanza. Si potrebbe restringere alle sole sequenze ordinate, , e il vincolo (2) diventerebbe implicito.
Radici quadrate di un numero complesso non nullo (domanda tipica di prima parte). L'istanza è un numero complesso : . Ogni ha due radici quadrate distinte, e , entrambe diverse da zero. Se la soluzione è la coppia delle due radici, , e Le soluzioni effettivamente usate stanno nel sottoinsieme . Anche qui non è univoca: per sono soluzioni sia sia , perché la coppia è ordinata. Le altre alternative del test non vanno: conterrebbe una sola radice (è il problema "trova una radice"), avrebbe coppie in ingresso, e non descrive i numeri complessi non nulli.
Analisi di un algoritmo
Di ogni algoritmo si studiano correttezza e complessità (in tempo e a volte in spazio, cioè la memoria necessaria).
Definizione (correttezza). Provare che è corretto per significa dimostrare Informalmente: la funzione di ingresso e uscita realizzata dall'algoritmo è coerente con la specifica.
Lo strumento principale è l'induzione: sulla taglia per gli algoritmi ricorsivi (Divide and conquer - proprietà di sottostruttura e albero delle chiamateUn algoritmo divide and conquer nasce da una proprietà di sottostruttura (funzioni Divide e Conquer, base n0) con sottoistanze di taglia strettamente minore; l'albero delle chiamate ne descrive l'esecuzione, e il D&C non ha memoria (ripete le sottoistanze uguali). Esempio completo: MAX di un array, con correttezza per induzione, ricorrenza e conteggio esatto n-1 tramite il teorema sugli alberi binari pieni.Divide and conquer - proprietà di sottostruttura e albero delle chiamate →), sugli invarianti per quelli iterativi (Dimostrazioni, induzione e invariantiTecniche di dimostrazione (esempio, controesempio, assurdo), induzione con casi base multipli, invarianti di ciclo (inizializzazione, conservazione, uso alla fine), schema generale per provare la correttezza; esempi svolti su arrayMax, sequenza di bit e numeri di Fibonacci.Dimostrazioni, induzione e invarianti →).
Definizione (modello di costo). Un modello di costo associa a ogni istruzione del modello computazionale un costo non negativo . Il costo dell'esecuzione di sull'istanza è
Nel corso si usano costi unitari o nulli: essendo interessati all'ordine di grandezza, si ignorano le costanti e si conta solo il tipo di istruzione che si ritiene dominante. La scelta va fatta con giudizio, perché da lei dipende se l'analisi è significativa:
- aritmetica su interi in binario: le operazioni elementari tra bit;
- aritmetica su matrici di numeri complessi: le operazioni aritmetiche tra numeri complessi;
- ordinamento: il confronto tra due chiavi.
I conteggi esatti che si incontrano nel corso (per esempio confronti per il massimo di elementi) valgono nel modello in cui si è dichiarato quale istruzione costa e quali costano .
Definizione (taglia). La taglia di un'istanza, , è una misura intera non negativa ragionevolmente rappresentativa della grandezza della codifica dell'istanza.
Non è una definizione rigorosa: la taglia si sceglie volta per volta.
- Due interi : il numero totale di bit per rappresentarli.
- Sequenza di interi da ordinare: .
- Due matrici quadrate : indifferentemente oppure (numero di elementi di una matrice), perché entrambe descrivono bene la grandezza.
Si pone . Poiché varia da un'istanza all'altra, lo si riassume con una funzione della sola taglia:
Definizione (tre complessità).
- Caso peggiore: .
- Caso migliore: .
- Caso medio: , media rispetto a una distribuzione fissata su .
Il caso peggiore è conservativo (limita il costo di qualsiasi esecuzione di taglia ) ed è quello usato di default nel corso. Il caso migliore di solito dice poco; il caso medio è molto difficile da stimare in teoria ma può essere prezioso in pratica.
Esempio (ricerca lineare). Cerco scandendo un array di elementi distinti, costo unitario per ogni confronto :
def lineare(A, x):
confronti = 0
for v in A:
confronti += 1
if v == x:
break
return confronti
A = list(range(10))
print(lineare(A, 0)) # caso migliore: 1
print(lineare(A, -1)) # caso peggiore (assente): 10
print(sum(lineare(A, x) for x in A) / len(A)) # medio, x presente e uniforme: 5.5Caso migliore (l'elemento è il primo), caso peggiore (l'elemento è l'ultimo o manca), caso medio se è in posizione uniforme: .
Esempio (la taglia di un intero conta). Per decidere se un intero è primo, la divisione per tentativi esegue circa divisioni (per , che è primo, ne servono ). Se come taglia si prende stesso, il costo è ; ma la taglia della codifica binaria è bit, e in funzione di il costo è : esponenziale nella taglia. Un algoritmo che sembra radice quadrata è dunque esponenziale rispetto alla grandezza dell'ingresso. Lo stesso accade quando è il numero da calcolare e non la lunghezza di una sequenza: lo si ritrova nel conteggio del calcolo memoizzato di una ricorrenza in Esercizio - funzione g(n) definita per ricorrenza, calcolo memoizzato e formula chiusa (compito del 19 giugno 2024, es. 2).
Sommario: che cosa specificare
Per analizzare correttamente un algoritmo bisogna sempre dire:
- quali sono le istruzioni a costo unitario;
- che cosa si intende per taglia ;
- quale complessità si studia (per default: tempo, caso peggiore).
Il passo seguente è ottenere per gli algoritmi ricorsivi, cioè scrivere e risolvere una ricorrenza: Divide and conquer - proprietà di sottostruttura e albero delle chiamateUn algoritmo divide and conquer nasce da una proprietà di sottostruttura (funzioni Divide e Conquer, base n0) con sottoistanze di taglia strettamente minore; l'albero delle chiamate ne descrive l'esecuzione, e il D&C non ha memoria (ripete le sottoistanze uguali). Esempio completo: MAX di un array, con correttezza per induzione, ricorrenza e conteggio esatto n-1 tramite il teorema sugli alberi binari pieni.Divide and conquer - proprietà di sottostruttura e albero delle chiamate →.
Domande d'esame
- Sia il problema dell'estrazione delle radici quadrate di un numero complesso non nullo. Si definiscano e . Risposta: , (la coppia delle due radici, ciascuna non nulla); con . Vedi Esercizio - domande di prima parte su problemi computazionali, ricorrenze e conteggi (simulazioni d'esame).
- Un problema computazionale è una funzione? Che relazione c'è con l'algoritmo? Risposta: no, è una relazione totale e non necessariamente univoca; l'algoritmo è una funzione che a ogni istanza associa una sola delle sue soluzioni.
- Che cosa si deve specificare prima di dichiarare "l'algoritmo costa "? Risposta: istruzioni a costo unitario, taglia dell'istanza e tipo di complessità (caso peggiore, migliore o medio).
Versione ripasso
- Algoritmo = funzione , finita e deterministica; problema : relazione totale () e non necessariamente univoca. risolve se , e : da una relazione estrae una funzione. sono codifiche di .
- Esempi: somma (, , funzione); raggiungibilità (cammino da a oppure , non univoca); ordinamento (, vincoli: stessa lunghezza, ordinata, permutazione); radici quadrate di : , , , non univoca perché la coppia è ordinata.
- Correttezza: ; per induzione (ricorsivi) o con invarianti (iterativi), vedi Dimostrazioni, induzione e invariantiTecniche di dimostrazione (esempio, controesempio, assurdo), induzione con casi base multipli, invarianti di ciclo (inizializzazione, conservazione, uso alla fine), schema generale per provare la correttezza; esempi svolti su arrayMax, sequenza di bit e numeri di Fibonacci.Dimostrazioni, induzione e invarianti →.
- Costo: con costi o sulle istruzioni dominanti (confronti per ordinare, operazioni complesse per matrici complesse, bit per interi). Taglia scelta con giudizio ( per sequenze, o per matrici, numero di bit per interi).
- Casi: peggiore (default), migliore , medio . Ricerca lineare su elementi: , , .
- Taglia di un intero: divisione per tentativi divisioni con bit: esponenziale nella taglia.
- Sommario: specificare istruzioni a costo unitario, taglia, tipo di complessità.
- Errori tipici: dire che è una funzione; scegliere per le due radici; contare tutte le istruzioni invece delle dominanti; confondere il valore con la taglia in bit.
- Raggiungibilità: , = sequenze di nodi ; contiene per ogni cammino da a (anche il cammino degenere ) e se non ce ne sono: più cammini, quindi relazione non univoca.
- Ordinamento: (stella di KleeneInsieme di tutte le sequenze finite, vuota compresa, di elementi di A.); biunivoca con . Non serve che ogni elemento di sia in relazione con un'istanza; in alternativa = sole sequenze ordinate e il vincolo diventa implicito.
- Costo e taglia, scelte tipiche: interi in binario operazioni su bit, taglia = bit; matrici complesse operazioni su complessi, taglia o ; ordinamento confronti, taglia . Le complessità si leggono come funzioni con ordine di grandezza in Notazione asintoticaDefinizioni di O, Omega, Theta e o piccolo con le costanti c ed n0; esempi con costanti esplicite; proprietà (polinomi, esponenziali, logaritmi, somme, implicazioni tra notazioni); sommatorie notevoli; terminologia (logaritmica, lineare, polinomiale, esponenziale).Notazione asintotica → e 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 →.
- Ricerca lineare (, costo = confronti): migliore , peggiore (assente), medio .
- Domande tipiche: definire ed per le radici quadrate (quarta alternativa, risposta in Esercizio - domande di prima parte su problemi computazionali, ricorrenze e conteggi (simulazioni d'esame)); dire che cosa specificare per analizzare un algoritmo (istruzioni a costo unitario, taglia, tipo di complessità).
Esercizi su questo argomento
- Esercizio - domande di prima parte su problemi computazionali, ricorrenze e conteggi (simulazioni d'esame)
- Esercizio - due elementi più piccoli di una stringa con divide and conquer (simulazione d'esame 2023-24, problema 1)
- Esercizio - funzione g(n) definita per ricorrenza, calcolo memoizzato e formula chiusa (compito del 19 giugno 2024, es. 2)