CAPITOLO 15.

Slides:



Advertisements
Presentazioni simili
INFORMATICA Algoritmi fondamentali
Advertisements

Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
                      Insertion-Sort
Ricorsione Procedure e funzioni ricorsive. Definizioni Un oggetto si dice ricorsivo se è definito totalmente o parzialmente in termini di sé stesso La.
Procedure e funzioni ricorsive
PROGRAMMARE IN PASCAL (le basi)
I File di testo in Pascal
Algoritmi e Programmazione
Informatica Generale Marzia Buscemi
1 Informatica Generale Susanna Pelagatti Ricevimento: Mercoledì ore presso Dipartimento di Informatica, Via Buonarroti,
Lez. 41 Universita' di Ferrara Facolta' di Scienze Matematiche, Fisiche e Naturali Laurea Specialistica in Informatica Algoritmi Avanzati Programmazione.
RB-alberi (Red-Black trees)
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Usa la tecnica del.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti ottimi.
Algoritmi e Strutture Dati
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Usa la tecnica del.
Capitolo 4 Ordinamento Algoritmi e Strutture Dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Usa la tecnica del.
Algoritmi e Strutture Dati Capitolo 2 Modelli di calcolo e metodologie di analisi.
Iterazione enumerativa (for)
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Lab 2 – Info B Marco D. Santambrogio – Riccardo Cattaneo –
File.
Algoritmo di Ford-Fulkerson
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 4 Ordinamento:
Algoritmi e strutture Dati - Lezione 7
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Algoritmi e Strutture Dati Capitolo 2 Modelli di calcolo e metodologie.
Capitolo 4 Ordinamento: Selection e Insertion Sort Algoritmi e Strutture Dati.
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
Capitolo 4 Ordinamento: Selection e Insertion Sort Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati
Capitolo 4 Ordinamento: Selection e Insertion Sort Algoritmi e Strutture Dati.
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
CORSO DI PROGRAMMAZIONE II Introduzione alla ricorsione
CORSO DI PROGRAMMAZIONE II
Algoritmi e Strutture Dati (Mod. B)
Algoritmi e Strutture Dati (Mod. A)
Alberi di Ricorrenza Gli alberi di ricorrenza rappresentano un modo conveniente per visualizzare i passi di sostitu- zione necessari per risolvere una.
Algoritmi e Strutture Dati Introduzione. Gli argomenti di oggi Analisi della bontà degli algoritmi Modello Computazionale Tempo di esecuzione degli algoritmi.
Algoritmi e Strutture Dati (Mod. B)
e array a più dimensioni
Il linguaggio Fortran 90: 4. Array: Vettori e Matrici
Introduzione alla programmazione lll
07/04/2003Algoritmi Ricerca in una sequenza di elementi Data una sequenza di elementi, occorre verificare se un elemento fa parte della sequenza oppure.
Modello dati ALBERO Albero: Albero: insieme di punti chiamati NODI e linee chiamate EDGES EDGE: linea che unisce due nodi distinti Radice (root): in una.
Heap binomiali Gli heap binomiali sono strutture dati su cui si possono eseguire efficientemente le operazioni: Make(H) : crea uno heap vuoto Insert(H,
Algoritmi su Tipi Semplici
Relatori: Emanuele e Denis Tornei Informatici. Introduzione In queste prime lezioni abbiamo affrontato linformatica procedendo a soluzioni di problemi,
OPERAZIONI CON STRINGHE Le operazioni più interessanti da fare, per ora, con le stringhe sono: determinare la lunghezza della stringa, cioè quanti caratteri.
CAPITOLO 7.
FILE TESTO OUTPUT INPUT + DATI PROGRAMMA OUTPUT INPUT PROGRAMMA CARICAMENTO DATI FILE DATI.
CAPITOLO 10.
Esercizio 10.* Un cassiere vuole dare un resto di n centesimi di euro usando il minimo numero di monete. a) Descrivere un algoritmo goloso per fare ciò.
Radix-Sort(A,d) // A[i] = cd...c2c1
Passo 3: calcolo del costo minimo
Introduzione agli algoritmi e strutture dati 3/ed T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein Copyright © 2010 – The McGraw-Hill Companies srl.
La complessità media O(n log n) di Quick-Sort vale soltanto se tutte le permutazioni dell’array in ingresso sono ugualmente probabili. In molte applicazioni.
Ugo de'Liguoro - Informatica 2 a.a. 03/04 Lez. 1 Cicli ed asserzioni Corso di Informatica 2 a.a. 2003/04 Lezione 1.
La scomposizione col metodo di Ruffini
La ricorsione.
GLI ALGORITMI VISIBILE SUL BLOG INFORMATICA ANNO SCOLASTICO 2013 / 2014 GABRIELE SCARICA 2°T.
Top STACK Top Le operazioni fondamentali che si fanno sugli stack sono: riempimento e svuotamento. Questo implica che durante lo svolgimento del programma.
Allievi Elettrici - AA Le funzioni ricorsive in C
Ordinamento in tempo lineare Il limite inferiore Ω(n log n) vale per tutti gli algoritmi di ordinamento generali, ossia per algoritmi che non fanno alcuna.
Algoritmi e Strutture Dati
Paola Disisto, Erika Griffini, Yris Noriega.  Insieme ordinato di operazioni non ambigue ed effettivamente computabili che, quando eseguito, produce.
CORSO DI PROGRAMMAZIONE II
1 Ordinamento (Sorting) INPUT: Sequenza di n numeri OUTPUT: Permutazione π = tale che a 1 ’  a 2 ’  … …  a n ’ Continuiamo a discutere il problema dell’ordinamento:
1 Ordinamento (Sorting) Input: Sequenza di n numeri Output: Permutazione π = tale che: a i 1  a i 2  ……  a i n Continuiamo a discutere il problema dell’ordinamento:
Problema dell’Ordinamento. Problema dell’ordinamento Formulazione del problema –Si vuole ordinare una lista di elementi secondo una data proprietà P Esempio:
GLI ALGORITMI DI ORDINAMENTO
Transcript della presentazione:

CAPITOLO 15

DIMOSTRAZIONI PER INDUZIONE La dimostrazione per induzione è una tecnica per provare che un asserto S(n) vale per tutti gli interi n maggiori di un certo limite inferiore. Supposto vero l’asserto la dimostrazione consiste in: individuare un caso base, il minimo valore di n, diciamo k, per cui si dimostra l’asserto S(k) dimostrare il passo induttivo, cioè che per ogni n  k , dove S(k) è la base induttiva, S(n) implica S(n+1) o equivalentemente supposto vero S(n) dimostriamo che è vero S(n+1). Esempio Vogliamo dimostrare che S(n): caso base Poniamo n=0 avremo che è quindi dimostrato vero

 c.v.d. passo induttivo Dobbiamo ora dimostrare che Il membro sinistro può essere riscritto come a) b) Avendo supposto vero l’asserto  Sostituiamo b) in a) c.v.d.

RICORSIVITA’ Algoritmo ricorsivo Un algoritmo è ricorsivo quando per trovare la soluzione ad un dato problema fa uso della soluzione trovata per lo stesso problema presentato in una versione più ridotta.

ESEMPIO Problema del Massimo Comun Divisore (MCD o GCD) Dati due numeri interi m ed n trovare il più grande intero positivo che divide sia m che n. Soluzioni possibili Scomposizione in fattori primi Algoritmo di Euclide

M ALGORITMO DI EUCLIDE N R R R’ R’ R’ MCD=

Pseudo codice IF M e/o N sono valori che rappresentano una soluzione valida THEN GCD  valore della soluzione (M o N) ELSE GCD  GCD(N, M MOD N)

PROGRAM Euclide; VAR M,N:integer; BEGIN writeln('Assegna M e N '); read(M);read(N); writeln('Il MCD e'':= ',MCD(M,N)); readln END. FUNCTION MCD(M,N:integer):integer; VAR Resto:integer; BEGIN Resto:=M MOD N; WHILE Resto<>0 DO M:=N; N:=Resto; Resto:=M MOD N END; MCD:=N FUNCTION GCD(M,N:integer):integer; BEGIN IF N=0 THEN GCD:=M ELSE GCD:=GCD(N,M MOD N) END; writeln('Il GCD e'':= ',GCD(M,N));

GCD:=GCD(N, M MOD N) GCD(66,48) GCD(48,18) GCD(18,12) GCD(12,6) No N=0? No N=0? No N=0? No N=0? Si GCD GCD GCD GCD GCD(48,18) GCD(18,12) GCD(12,6) GCD(6,0) GCD 6 Turbo Pascal Version 7.0 Assegna M e N 66 48 **push** GCD(66,48) **push** GCD(48,18) **push** GCD(18,12) **push** GCD(12,6) **pop** N= 0 **pop** N= 6 **pop** N= 12 **pop** N= 18 Il GCD e':= 6 FUNCTION GCD(M,N:integer):integer; BEGIN IF N=0 THEN GCD:=M ELSE writeln(’**push** GCD(',M,',',N,')'); writeln; GCD:=GCD(N,M MOD N); writeln(’**pop** N= ',M MOD N) END END;

Ogni algoritmo ricorsivo deve essere tale che durante i passi della ricorsione, cioè della riduzione del problema, si giunge sempre ad una soluzione. Questa soluzione è detta CASE BASE o STOPPING CASE. Nel caso del GCD il CASE BASE è il caso in cui N=0. CASE BASE : è una soluzione per un algoritmo ricorsivo per la quale non sono necessarie ulteriori chiamate ricorsive. Ogni algoritmo ricorsivo, per essere valido, richiede almeno un CASE BASE. Il meccanismo di gestione delle chiamate ricorsive è quello dello stack, si fa cioè ricorso alla tecnica LIFO ed è chiamato Run-time stack.

Un esempio di ricorsione per accumulazione Problema Si vuole calcolare la potenza Nma del numero reale Xre. Soluzione Base Case Ricorsione FUNCTION PotenzaN(Xre:real,N:integer):real; BEGIN IF N=0 THEN PotenzaN :=1 ELSE PotenzaN:= Xre*PotenzaN(Xre,N-1); END;

PotenzaN(0.9,3) Turbo Pascal Version 7.0 Assegna Xre e N 0.9 3 No PotenzaN(0.9,2) PotenzaN(0.9,1) N=0? No N=0? No PotenzaN(0.9,0) N=0? Si PotenzaN 1 PotenzaN 0.9* PotenzaN 0.9* PotenzaN 0.9* Turbo Pascal Version 7.0 Assegna Xre e N 0.9 3 **push** 3 **push** 2 **push** 1 **pop** 1 **pop** 2 **pop** 3 0.900 elevato a 3 e'= 0.729 FUNCTION PotenzaN(Xre:real,N:integer):real; BEGIN IF N=0 THEN PotenzaN :=1 ELSE writeln(‘**push** ‘,N); PotenzaN:= Xre*PotenzaN(Xre,N-1); writeln(‘**pop** ‘,N); END;

COME FUNZIONA LA RICORSIVITA’ PROCEDURE(p1 ,…., pk ) Case base ? NO allora applica Applica la soluzione a PROCEDURE(p’1 ,…., p’k ) Case base ? NO allora applica Applica la soluzione a PROCEDURE(p”1 ,…., p”k ) Case base ? NO allora applica PROCEDURE(p*1 ,…., p*k ) Case base ? SI allora applica la soluzione a Dove (pi1 ,…., pik ) sono problemi ridotti del problema precedente

Possiamo dire che è stato applicato il metodo del IF i parametri fanno riferimento a un Case Base THEN risolvi il problema ELSE usa i valori dei parametri per un problema ridotto CHIAMA LA PROCEDURA O FUNZIONE PER RISOLVERE IL PROBLEMA RIDOTTO Possiamo dire che è stato applicato il metodo del DIVIDE ET IMPERA

Un algoritmo iterativo consiste in un unico processo che ripete le stesse identiche operazioni molte volte. Un algoritmo ricorsivo consiste in un numero finito di processi aperti uno dopo l’altro e posti in uno stack. Non appena si chiude un processo subito si sale nello stack e si chiude il processo immediatemente precedente e così via di seguito.

1. Esiste almeno un case base la cui soluzione è banale Per scrivere un algoritmo ricorsivo bisogna soddisfare le seguenti condizioni: 1. Esiste almeno un case base la cui soluzione è banale 2. Tutti i sottoproblemi devono poter essere risolti in termini di versioni ridotte di uno stesso problema 3. Le azioni applicate per la soluzione di un problema ridotto portano sempre alla soluzione di un problema più grande 4. In funzione di quanto sia grande il problema iniziale deve essere sempre possibile trovare almeno un case base nel corso della elaborazione del problema originale.

q.e.d. DIMOSTRAZIONE PER INDUZIONE Vogliamo dimostrare che S(n): a) caso base Poniamo n=1 avremo che è quindi dimostrato vero passo induttivo Dobbiamo ora dimostrare che b) Avendo supposto vero l’asserto sostituiamo in b) il valore che si ottiene da a)

DIMOSTRAZIONE PER INDUZIONE DELLA CORRETTEZZA DELL’ALGORTIMO RICORSIVO PER IL GCD caso base Poniamo GCD(M,0)=M passo induttivo Dobbiamo ora dimostrare che GCD(M’,N’)=GCD(N, M MOD N) Supponiamo GCD(M’,N’)=X allora M’=hX e N’=zX dove h e z sono interi essendo N’=M MOD N questo implica per definizione che M=kN+N’ dove k è un intero ma N’=zX allora M=kN+zX inoltre N=M’=hX quindi M=khX+zX=(kh+z)X=wX essendo allora sia M che N divisibili per X questo è il GCD(M,N)

DIMOSTRAZIONE PER INDUZIONE DELLA CORRETTEZZA DELL’ALGORTIMO RICORSIVO PER IL GCD caso base Poniamo GCD(M,0)=M passo induttivo Dobbiamo ora dimostrare che GCD(M’,N’)=GCD(N, M MOD N) essendo N’=M MOD N questo implica per definizione che Supponiamo GCD(M’,N’)=X allora M’=hX e N’=zX dove h e z sono interi ma N’=zX M=kN+N’ dove k è un intero allora M=kN+zX inoltre N=M’=hX quindi M=khX+zX=(kh+z)X=wX essendo allora sia M che N divisibili per X questo è il GCD(M,N) q.e.d.

Un algoritmo ricorsivo deve essere completo, deve cioè sempre esistere una soluzione qualunque sia l’input. La completezza dipende dal dominio su cui si definisce l’algoritmo. Esempio: PotenzaN se definito sugli interi non è completo perché non funziona per gli interi negativi (infatti N-1 per N negativo non raggiunge mai lo 0). Diventa completo se il domino di definizione è quello dei numeri interi positivi. Stack Infinito Si genera quando per un qualche input di un algoritmo ricorsivo non si raggiunge mai il CASE BASE.

Esempio Dato un testo scritto su un file e formato da più righe leggerlo e riscriverlo in ordine inverso rispetto alle righe. Es. La Vispa Teresa avea tra l’erbetta a volo sorpresa gentil farfalletta gentil farfalletta a volo sorpresa avea tra l’erbetta La Vispa Teresa Pseudo-codice reset(Teresa) InvertiRighe (Teresa)

PROGRAM InvertiRighe(Teresa,output); VAR Teresa:text; PROCEDURE StoreDisplay(VAR Teresa:text); {procedura ricorsiva: la prima linea letta è l’ultima mostrata a video} BEGIN ……….. END; PROCEDURE MostraRigheInvertite (VAR Teresa:text); BEGIN StoreDisplay(Teresa) END; { BODY } BEGIN reset(Teresa); MostraRigheInvertite END.

base case *push* La vispa Teresa *push* avea tra l'erbetta PROGRAM InvertiRighe(output,FInput); CONST LungMax=80; {massima lunghezza permessa alle stringhe } TYPE Stringa=STRING[LungMax]; VAR FInput: text; PROCEDURE StoreDisplay(VAR FInput:text); Rigo: Stringa; BEGIN IF NOT eof(FInput) THEN readln(FInput,Rigo); writeln('*push* ',Rigo); StoreDisplay(FInput); writeln(' *pop* ',Rigo) END END; PROCEDURE MostraRigheInvertite(VAR FInput:text); StoreDisplay(FInput) {BODY } assign(FInput,'C:\TP\ESEMPI\TERESA.TXT'); reset(FInput); MostraRigheInvertite(FInput); readln END. base case *push* La vispa Teresa *push* avea tra l'erbetta *push* a volo sorpresa *push* gentil Farfalletta *pop* gentil Farfalletta *pop* a volo sorpresa *pop* avea tra l'erbetta *pop* La vispa Teresa

From - Thu Aug 26 07:09:19 1999 Return-Path: <epontell@cs.nmsu.edu> Received: from di.unito.it (pianeta.di.unito.it) by sole.cib.na.cnr.it (4.1/SMI-4.1) id AA29055; Sun, 1 Aug 99 06:10:35 +0200 Received: from cs.CS.NMSU.Edu by di.unito.it (8.9.1a/SMI-INFODIP) id FAA23820; Sun, 1 Aug 1999 05:50:02 +0200 (MET DST) Received: from cs.nmsu.edu (epontell@pippo [128.123.64.31]) by cs.CS.NMSU.Edu (8.8.6/8.8.6) with ESMTP id VAA27225; Sat, 31 Jul 1999 21:53:44 -0600 (MDT) Sender: epontell@cs.nmsu.edu Message-Id: <37A3C4CA.7A997A9A@cs.nmsu.edu> Date: Sat, 31 Jul 1999 21:53:46 -0600 From: Enrico <epontell@cs.nmsu.edu> Organization: Laboratory for Logic & Databases X-Mailer: Mozilla 4.05 [en] (X11; I; Linux 2.0.32 i686) Mime-Version: 1.0 To: aiia@di.unito.it, ccl@ps.uni-sb.de, compulog@doc.imperial.ac.uk, compulognet-parimp@clip.dia.fi.upm.es, elsnet-list@let.ruu.nl, gulp@di.unipi.it, lpnmr@cs.engr.uky.edu, lprolog@central.cis.upenn.edu, clp@iscs.nus.edu.sg, lp-internet@doc.ic.ac.uk, concurrency@cwi.nl, dappia@di.fct.unl.pt Subject: Research Assistantships at NMSU Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-UIDL: b0fa9065af5ad288d1475b519b3ad95b Status: U X-Mozilla-Status: 0001 Applications are invited for several Research Assistant Positions in the Laboratory for Logic and Databases of the Dept. of Computer Science at New Mexico State University.

From - Thu Aug 26 07:09:19 1999 Return-Path: <epontell@cs.nmsu.edu> ............ X-Mozilla-Status: messaggio From - Thu Aug 28 09:09:29 1999 Return-Path: <ernb@sole.cib.na.cnr.it>

ElaboraTesto(Rigo) Pseudo codice IF Not eof(InFile) THEN readln(Finput,Rigo)

ALTRI ESEMPI FATTORIALE Supponiamo di avere 3 lettere a b c . Vogliamo sapere quante permutazioni si possono fare con questi 3 caratteri. - ci sono 3 maniere per scegliere quale lettera mettere in terza posizione (genericamente n) abc acb cba - per ognuna delle 3 scelte precedenti ci sono 2 maniere diverse per scegliere la lettere da mettere in seconda posizione in totale 3*2 (genericamente n*(n-1)) abc bac acb cab cba bca - per ognuna delle 6 scelte precedenti c’è 1 sola maniera per scegliere la lettere da mettere in prima posizione in totale 3*2*1 (genericamente n*(n-1)…..*1) FATTORIALE

FUNCTION Fattoriale(N:integer):integer; VAR Count, Product: integer; BEGIN Product:=1; FOR Count:=2 TO N DO Product:=Product*Count; Fattoriale:=Product END; FUNCTION Fattoriale (N:integer):integer; BEGIN IF N=0 THEN Fattoriale:=1 ELSE Fattoriale:=N*Fattoriale(N-1) END;

Sommatoria dei primi N interi positivi 1. La somma dei primi 0 interi positivi vale 0. 2. La somma dei primi N interi positivi è uguale alla somma dei primi N-1 interi più N. FUNCTION Sommatoria (N:integer):integer; BEGIN IF N=0 THEN Sommatoria:=1 ELSE Sommatoria :=N+Sommatoria(N-1) END;

Quando si applica un processo ricorsivo tipo quello della accumulazione bisogna assicurarsi che i valori accumulati nelle relative variabili siano correttamente passati da un processo all’altro. Inoltre il valore assunto da una variabile in un processo ricorsivo non deve essere distrutto dal lancio di un altro processo ricorsivo. Di qui la necessità di passare le variabili utilizzando la chiamata per VAR. Esempio: Fare la somma dei primi N interi positivi e mostrare le somme parziali ogni M passi. Pseudo-codice IF N=0 THEN scrivi un messaggio Somma  0 ELSE ShowSums(N-1, M, Sum) Somma  Somma+N IF N MOD M=0 THEN scrivi N e Somma

PROGRAM SommaRicorsiva(inputoutput); VAR Nin,Min,Sumout:integer; PROCEDURE ShowSums(N,M:integer; VAR Sum:integer); Temp:integer; BEGIN IF N=0 THEN writeln(' Somme parziali '); Sum:=0 END ELSE ShowSums(N-1,M,Sum); Sum:=Sum+N; Temp:=N MOD M; IF N MOD M=0 THEN writeln('La somma dei primi ',N:1,' numeri e'' = ',Sum:1,'.') END; BEGIN writeln(' Assegna N e M '); readln(Nin); readln(Min); writeln(' N M Sum'); ShowSums (Nin,Min,Sumout); writeln('La somma dei primi ',Nin:1,' numeri e'' = ', Sumout:1,'.'); END.

Assegna N e M 7 2 N M Sum push** 7 2 0 push** 6 2 0 push** 5 2 0 Somme parziali pop** 1 2 0 pop** 2 2 1 La somma dei primi 2 numeri e' = 3. pop** 3 2 3 pop** 4 2 6 La somma dei primi 4 numeri e' = 10. pop** 5 2 10 pop** 6 2 15 La somma dei primi 6 numeri e' = 21. pop** 7 2 21 La somma dei primi 7 numeri e' = 28.

Esempio con due CASE BASE Problema: Assegnare agli elementi dell’Array di interi Ints, dei numeri compresi nell’intervallo 1..TotalAssigned. Ogni numero viene dato da tastiera. Il processo di lettura cessa o quando si introducono tutti i numeri concessi (MaxElements) oppure quando si introduce un numero negativo. Subito dopo si effettua l’assegnazione. In questo caso i CASE BASE possibili sono due: abbiamo letto il massimo numero possibile di valori abbiamo letto un numero negativo In entrambi i casi la lettura deve terminare e si effettua l’assegnazione

Pseudo-Codice {Indichiamo con Left quanti numeri positivi è ancora possibile assegnare e con Temp il valore letto } IF Left = 0 THEN gestisci il CASE BASE N°1 ELSE read(Temp) IF Temp<=0 THEN gestisci il CASE BASE N°2 istruzioni prima della ricorsione FillIn(Left-1,TotalAssigned,Ints) istruzioni dopo la ricorsione TotalAssigned = 0 Temp< = 0 TotalAssigned = TotalAssigned + 1 Ints[TotalAssigned ]  Temp

PROGRAM CaseBase2(input,output); CONST MaxElements=4; TYPE IntsArray=ARRAY[1..MaxElements] OF integer; VAR Ints:IntsArray; Left, TotalAssigned, I:integer; PROCEDURE FillIn(Left:integer; VAR TotalAssigned:integer; VAR Ints:IntsArray); Temp: integer; BEGIN IF Left=0 THEN readln; writeln('Non possono essere letti altri valori. '); TotalAssigned:=0; END ELSE read(Temp); IF Temp <=0 THEN writeln('E'' stato introdotto un numero negativo. '); FillIn(Left-1, TotalAssigned,Ints); TotalAssigned:= TotalAssigned+1; Ints[TotalAssigned]:=Temp END;

{BODY} BEGIN writeln('Inizia inserzione dati max= ',MaxElements); writeln; FillIn(MaxElements,TotalAssigned,Ints); writeln(' ARRAY '); FOR I:=1 TO TotalAssigned DO writeln(Ints[I]); readln END. Inizia inserzione dati max= 4 Left TotalAssigned 1 push** 4 0 2 push** 3 0 3 push** 2 0 4 push** 1 0 Non possono essere letti altri valori. pop** 1 0 pop** 2 1 pop** 3 2 pop** 4 3 ARRAY Inizia inserzione dati max= 4 Left TotalAssigned 11 push** 4 0 12 push** 3 0 -2 E' stato introdotto un numero negativo. pop** 3 0 pop** 4 1 ARRAY

come si passano i valori di variabili Alcuni suggerimenti Fatti importanti come si passano i valori di variabili a- usare una chiamata per valore per determinare se il CASE BASE è verificato b- se il processo ricorsivo è di tipo per accumulazione usare la chiamata per VAR per la variabile di accumulazione c- usare una variabile locale se il suo valore è istanziato all’interno del processo ricorsivo per cui ad ogni passo della ricorsione riprende il valore di partenza PROCEDURE FillIn(Left:integer; VAR TotalAssigned:integer; VAR Ints:IntsArray); VAR Temp: integer;

IF Not eof(InFile) THEN readln(Finput,Rigo) l’ordine con cui le istruzioni vengono eseguite, se prima o dopo la chiamata ricorsiva A- se una o più istruzioni riducono la dimensione del problema esse devono precedere la chiamata ricorsiva B- se una o più istruzioni necessitano del risultato della ricorsione vanno b- poste dopo la chiamata ricorsiva ElaboraTesto(Rigo) Pseudo codice IF Not eof(InFile) THEN readln(Finput,Rigo) BEGIN read(Temp); IF Temp <=0 THEN writeln('E'' stato introdotto un numero negativo. '); TotalAssigned:=0; END ELSE FillIn(Left-1, TotalAssigned,Ints); TotalAssigned:= TotalAssigned+1; Ints[TotalAssigned]:=Temp

PROCEDURE FillIn2(Left:integer; VAR Ints:IntsArray); VAR PROCEDURE FillIn(Left:integer; VAR TotalAssigned:integer; VAR Ints:IntsArray); VAR Temp: integer; BEGIN IF Left=0 THEN writeln('Non mettere altri valori. '); TotalAssigned:=0; END ELSE read(Temp); IF Temp <=0 THEN writeln('E'' stato introdotto un negativo. '); FillIn(Left-1, TotalAssigned,Ints); TotalAssigned:= TotalAssigned+1; Ints[TotalAssigned]:=Temp END; PROCEDURE FillIn2(Left:integer; VAR Ints:IntsArray); VAR Temp: integer; BEGIN IF Left<>0 THEN FillIn(Left-1,Ints); read(Temp); Ints[Left]:=Temp END END; FillIn2 push** 5 push** 4 push** 3 push** 2 push** 1 pop** 1 10 pop** 2 20 pop** 3 30 pop** 4 40 pop** 5 50 ARRAY FillIn3 push** 5 10 push** 4 20 push** 3 30 push** 2 40 push** 1 50 pop** 1 pop** 2 pop** 3 pop** 4 ARRAY FillIn1 10 push** 5 20 push** 4 30 push** 3 40 push** 2 50 push** 1 pop** 1 pop** 2 pop** 3 pop** 4 pop** 5 ARRAY PROCEDURE FillIn3(Left:integer; VAR Ints:IntsArray); VAR Temp: integer; BEGIN IF Left<>0 THEN read(Temp); FillIn(Left-1,Ints); Ints[Left]:=Temp END END;

FillIn1 10 push** 5 20 push** 4 30 push** 3 40 push** 2 50 push** 1 pop** 1 pop** 2 pop** 3 pop** 4 pop** 5 ARRAY FillIn2 push** 5 push** 4 push** 3 push** 2 push** 1 pop** 1 10 pop** 2 20 pop** 3 30 pop** 4 40 pop** 5 50 ARRAY FillIn3 push** 5 10 push** 4 20 push** 3 30 push** 2 40 push** 1 50 pop** 1 pop** 2 pop** 3 pop** 4 ARRAY

Ricorsione lineare = al massimo una chiamata ricorsiva per blocco Ricorsione non lineare = più di una chiamata ricorsiva per blocco Per capire bene come opera una ricorsione non lineare si traccia un albero che mostra la storia dello stack.

{BODY} MAIN MAIN Esempio PROGRAM TreeExample(output); PROCEDURE C(N:integer); BEGIN IF N>0 THEN C(N-1) END; PROCEDURE B; C(1) PROCEDURE A; B {BODY} A; C(1); writeln(‘Fine’) END. Esecuzione 1 main chiama A ; 2 A chiama B; 3 B chiama C(1); 4 C(1) chiama C(0); 5 C(0) ritorna C(1); 6 C(1) ritorna B; 7 B ritorna A; 8 A ritorna al main; 9 main chiama C(1); 10 C(1) chiama C(0); 11 C(0) ritorna C(1); 12 C(1) ritorna main; 13 main chiama writeln(‘Fine’) 14 writeln(‘Fine’) MAIN PROCEDURE A PROCEDURE B MAIN PROCEDURE C(1) PROCEDURE C(1) PROCEDURE C(0) PROCEDURE C(0)

main chiama A A chiama B B chiama C(1) C(1) chiama C(0) PROGRAM TreeExample(output); PROCEDURE C(N:integer); BEGIN IF N>0 THEN writeln('C(',N,') chiama C(',N-1,')'); C(N-1); writeln(' C(',N-1,') ritorna a C(',N,')') END END; PROCEDURE B; writeln('B chiama C(1)'); C(1); writeln(' C(1) ritorna a B') PROCEDURE A; writeln('A chiama B'); B; writeln('B ritorna A') writeln('main chiama A'); A; writeln('A ritorna a main'); writeln('main chiama C(1)'); writeln('C(1) ritorna a main'); writeln('main chiama Fine'); writeln('Fine'); readln END. main chiama A A chiama B B chiama C(1) C(1) chiama C(0) C(0) ritorna a C(1) C(1) ritorna a B B ritorna A A ritorna a main main chiama C(1) C(1) ritorna a main main chiama Fine Fine

main main chiama Fine writeln main chiama A A Fine ritorna a main A ritorna a main main chiama C(1) C(1) C(1) ritorna a main A chiama B B B ritorna A B chiama C(1) C(1) C(1) ritorna a B C(0) ritorna a C(1) C(1) chiama C(0) C(0) C(1) chiama C(0) C(0) C(0) ritorna a C(1)

main writeln A B C(1) C(1) C(0) C(0)

Regole per costruire l’albero della storia dello stack 1. Ogni albero deve avere una radice principale 2. L’albero è fatto di nodi che rappresentano i processi che sono eseguiti ad un certo passo. Ogni nodo rappresenta esattamente un processo. Se è necessario si mettono etichette che indicano il processo. 3. Un ramo dell’albero rappresenta una chiamata e un ritorno di un processo rispetto ad un altro. Se ci sono più processi ricorsivi si indicano in ordine a partire da sinistra. 4. Ogni nodo è visitato una sola volta (push e pop) e nessun processo può terminare se prima non sono terminati tutti quelli che ha chiamato.

OPERATORI SULLE STRINGHE NullString {ritorna una la stringa nulla ''.} ConvertSysString {converte una stringa rappresentata in un qualche sistema nella stringa equivalente di type StringADT} ReadCh {legge i caratteri di una stringa da un file e se supera la lunghezza prefissata o trova eoln restituisce un carattere sentinella} ReadString {legge la stringa da un file escludendo eventuali caratteri sentinella} ReadlnString {legge una stringa da una linea di un file predeterminato} Ach {ritorna il carattere N-esimo di una stringa } StrLength {ritorna la lunghezza della stringa} WriteString {scrive una stringa in un file} WritelnString {scrive una stringa in un file seguita da un <eoln>} LowerCase {trasforma le maiuscole in minuscole} StrEqual {ritorna TRUE se due stringhe hanno gli stessi caratteri e la stessa lunghezza} StrExtract {copia una stringa di una predeterminata lunghezza a partire da una determinata posizione in una stringa di output} StartPos {Ritorna la posizione di partenza di una data sub-stringa nell'ambito di una preassegnata stringa} StrLessThan {ritorna TRUE se la prima stringa precede alfabeticamente la seconda} ChConcat {concatena un singolo carattere ad una stringa} StrConcat {concatena due stringhe} StrRemove {rimuove un predeterminato numero di caratteri a partire da una certa posizione di una stringa di input/output} StrInsert {inserisce un predeterminata stringa di caratteri a partire da una certa posizione in una variabile stringa.} ScriviSTringa {scrivi la stringa} OPERATORI SULLE STRINGHE

UNIT Stringa; INTERFACE CONST MaxLength=80; TYPE SysString=STRING[MaxLength]; StringADT=RECORD Chars:ARRAY[1.. MaxLength] OF char; Len:0.. MaxLength END;

FUNCTION StartPos(Substr, SearchStr:StringADT):integer; VAR {Ritorna la posizione di partenza di una data sub-stringa Substr nell'ambito di una preassegnata SearchStr stringa} VAR SLen, {numero di caratteri della sub-stringa} Pos: integer; {posizione di partenza della sub-stringa} Found: Boolean; CandStr: StringADT; {sub-stringa candidata } BEGIN SLen:=SubStr.Len; Found:=FALSE; Pos:=1; WHILE NOT (SearchStr.Len+1-Pos>SLen) AND NOT Found DO StrExtract(SearchStr,Pos,SLen,CandStr); IF StrEqual(CandStr,SearchStr) THEN Found:=TRUE ELSE Pos:=Pos+1 END; IF Found THEN StratPos:=Pos StratPos:=0 StringADT=RECORD Chars:ARRAY[1.. MaxLength] OF char; Len:0.. MaxLength END;

InStrPos, {Posizione di un carattere nella stringa di input} PROCEDURE StrExtract(InStr:StringADT; Start, TotalChs:integer; VAR OutStr: StringADT); {copia da una stringa di input InStr una stringa di una predeterminata lunghezza TotalChs a partire da una determinata posizione Start in una stringa di output OutStr} VAR InStrPos, {Posizione di un carattere nella stringa di input} OutStrPos:integer; {Posizione di un carattere nella stringa di output} BEGIN WITH OutStr DO IF Start > InStr.Len THEN {controlla che il punto di partenza non sia fuori stringa} Len:=0 ELSE IF TotalChs > InStr.Len+1-Start THEN Len:=InStr.Len+1-Start ELSE Len:=TotalChs; InStrPos:=Start; FOR OutStrPos:=1 TO Len DO Chars[OutStrPos]:=InStr.Chars[InStrPos]; InStrPos:=InStrPos+1 END END; StringADT=RECORD Chars:ARRAY[1.. MaxLength] OF char; Len:0.. MaxLength END;

FUNCTION StrEqual(Instr1, Instr2:StringADT):boolean; {ritorna TRUE se le stringhe Instr1 e Instr2 hanno gli stessi caratteri e la stessa lunghezza} VAR Pos, TotalChars:integer; StillEqual:boolean; BEGIN IF Instr1.Len<>Instr2.Len THEN StillEqual:= FALSE ELSE StillEqual:= TRUE; TotalChars:= Instr1.Len; Pos:=1; WHILE NOT(Pos>TotalChars) AND StillEqual DO IF Minuscole(InStr1.Chars[Pos])<> Minuscole(InStr2.Chars[Pos]) THEN Pos:=Pos+1; StrEqual:=StillEqual END; FUNCTION Minuscole(Ch:char):char; BEGIN IF Ch IN [‘A’..’Z’] THEN Minuscole:=chr(ord(Ch)+ord(‘a’)-ord(‘A’)) ELSE Minuscole:=ch END;

PROCEDURE ReadlnString (Sentinel:char; VAR OutStr:StringADT;VAR InFile:text); {legge una stringa da una linea di un file predeterminato} VAR Ch:char; BEGIN WITH InString DO Len:=0; WHILE NOT eoln(InFile) AND NOT (Len=MaxLength) DO Read(Infile,Ch); Len:=Len+1; Chars[Len]:=Ch; END END;

From - Thu Aug 26 07:09:19 1999 Return-Path: <epontell@cs.nmsu.edu> Received: from di.unito.it (pianeta.di.unito.it) by sole.cib.na.cnr.it (4.1/SMI-4.1) id AA29055; Sun, 1 Aug 99 06:10:35 +0200 Received: from cs.CS.NMSU.Edu by di.unito.it (8.9.1a/SMI-INFODIP) id FAA23820; Sun, 1 Aug 1999 05:50:02 +0200 (MET DST) Received: from cs.nmsu.edu (epontell@pippo [128.123.64.31]) by cs.CS.NMSU.Edu (8.8.6/8.8.6) with ESMTP id VAA27225; Sat, 31 Jul 1999 21:53:44 -0600 (MDT) Sender: epontell@cs.nmsu.edu Message-Id: <37A3C4CA.7A997A9A@cs.nmsu.edu> Date: Sat, 31 Jul 1999 21:53:46 -0600 From: Enrico <epontell@cs.nmsu.edu> Organization: Laboratory for Logic & Databases X-Mailer: Mozilla 4.05 [en] (X11; I; Linux 2.0.32 i686) Mime-Version: 1.0 To: aiia@di.unito.it, ccl@ps.uni-sb.de, compulog@doc.imperial.ac.uk, compulognet-parimp@clip.dia.fi.upm.es, elsnet-list@let.ruu.nl, gulp@di.unipi.it, lpnmr@cs.engr.uky.edu, lprolog@central.cis.upenn.edu, clp@iscs.nus.edu.sg, lp-internet@doc.ic.ac.uk, concurrency@cwi.nl, dappia@di.fct.unl.pt Subject: Research Assistantships at NMSU Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-UIDL: b0fa9065af5ad288d1475b519b3ad95b Status: U X-Mozilla-Status: 0001 Applications are invited for several Research Assistant Positions in the Laboratory for Logic and Databases of the Dept. of Computer Science at New Mexico State University.

ElaboraTesto(Rigo) Pseudo codice IF Not eof(InFile) THEN readln(Finput,Rigo)

Problema Nel 1228 messer Leonardo Pisano si pose il seguente problema: posta una coppia di conigli in un recinto supponiamo che questa coppia dopo un mese genera un’altra coppia, questa a sua volta dopo un altro mese ne genera un’altra e contemporaneamente anche le precedenti generano altre coppie. Dopo un anno, cioè dopo 12 mesi quanti conigli ci saranno nel recinto? 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233

Esempio I numeri di Fibonacci definiti ricorsivamente. N=1 : Fib(N)  1 N=0 : Fib(N)  0 N>=2 : Fib(N)  Fib(N-2)+FIB(N-1) FUNCTION Fibonacci(N:integer):integer; {ritorna l’N-esimo numero di Fibonacci} BEGIN IF N=0 THEN Fibonacci:=0 ELSE IF N=1 THEN Fibonacci:=1 ELSE Fibonacci:= Fibonacci(N-2) + Fibonacci(N-1) END.

Ricorsione lineare: al più una chiamata ricorsiva nell’ambito di uno stesso processo ricorsivo. Ricorsione non lineare: più di una chiamata ricorsiva nell’ambito di uno stesso processo ricorsivo. main F(3) F(1) F(2) F(4) Fibonacci(4) main F(3) F(1) F(2) Fibonacci(3)

Complessità dell’algoritmo per il calcolo dei numeri di Fibonacci E’ stato dimostrato che per N abbastanza grande la complessità di F(N), cioè il numero di chiamate ricorsive, è circa O(1.61N) N 1,61^N 1 MFLOP 1 TERAFLOP 1 2 10 117 20 13.694 0,01 sec 40 187.514.580 3,13 minuti 50 21.942.888.767 6,10 ore 100 481.490.367.451.071.000.000 15.480.014 anni 15.480 200 231.832.973.948.168.000.000.000.000.000.000.000.000.000 7,45348E+27 anni 7,45E+27

Fibonacci(6) main F(6) F(4) F(5) F(4) F(3) F(2) F(3) F(2) F(3) F(1)

PROGRAM ContaCall(output); VAR Calls:real; No:integer; FUNCTION Fibonacci(N:integer; VAR Calls:real):real; {ritorna l'N-esimo numero di Fibonacci} BEGIN Calls:=Calls+1; IF N=0 THEN Fibonacci:=0 ELSE IF N=1 THEN Fibonacci:=1 ELSE Fibonacci:= Fibonacci(N-2,Calls) + Fibonacci(N-1,Calls) END; {BODY} FOR No:=1 TO 40 DO Calls:=0; IF No MOD 5=0 THEN writeln('Fibonacci(',No:1,') = ', Fibonacci(No,Calls):20:0,' chiamate= ',Calls:20:0) readln END.

Fibonacci(5) = 3 chiamate= 9

Per ridurre da O (1.61N) a O(N) la complessità di calcolo per i numeri di Fibonacci invece di chiamare ricorsivamente la procedura di calcolo per ogni nodo dell’albero dello stack depositiamo i risultati di ogni computazione in un array e li richiamiamo, senza più calcolarli ogni volta che ne abbiamo bisogno. Detto FibNos l’array in cui si depositano i numeri parziali possiamo costruire una procedura ricorsiva alla seguente maniera: CASE BASE Quando N=2 allora poni FibNos[2]  1 FibNos[1]  0 CHIAMATA RICORSIVA FibNos[N]  FibNos[N-2] + FibNos[N-1]

Pseudo Codice IF N=“ THEN FibNos[2]  1 FibNos[1]  0 ELSE FibNos[N]  FibNos[N-2] + FibNos[N-1] Svantaggio Per N grande possiamo avere un errore di stack overflow

SetFibNo(4) FibNo[4] SetFibNo(3) FibNo[3] SetFibNo(2) FibNo[2] PROGRAM ContaCallVeloce(output); {calcola l'N-esimo numero di Fibonacci in O(N) } CONST MaxFib=50; TYPE FibNoArr=ARRAY[1..MaxFib] OF real; VAR FibNos:FibNoArr; No:integer; PROCEDURE SetFibNo(N:integer; VAR FibNos:FibNoArr); {ritorna l'N-esimo numero di Fibonacci} BEGIN IF N=2 THEN FibNos[2]:=1; FibNos[1]:=1 END ELSE SetFibNo(N-1,FibNos); FibNos[N]:=FibNos[N-2] + FibNos[N-1] END; {BODY} FOR No:=1 TO 40 DO IF No MOD 5=0 THEN SetFibNo(No,FibNos); writeln('Fibonacci(',No:1,') = ',No,FibNos[No]:20:0) readln END. SetFibNo(4) SetFibNo(3) SetFibNo(2) FibNo[4] FibNo[3] FibNo[2] FibNo[1] F(3) F(1) F(2) F(4)

ALGORITMI DI RICERCA LINEARE Problema: Cercare tra i valori contenuti in un Array un preassegnato valore. Esempio: Data una lista di N numeri verificare se esiste un preassegnato Valore. Condizioni di uscita: - la ricerca è finita se nessun elemento uguale a quello cercato esiste - la ricerca è finita se almeno un elemento uguale a quello cercato è stato trovato Partire dall’ultimo elemento della lista e risalire fino in cima nella ricerca dell’elemento.

Soluzione: Gestire opportunamente l’indice dell’array in cui sono contenuti gle elementi su cui fare la ricerca I criteri per stabilire il nuovo valore da attribuire all’indice possono essere i più diversi. E’ però importante che una volta stabilito che un elemento individuato da un certo indice non è quello cercato questo elemento non venga più esaminato. Algoritmo 10.2 Indice  NumeroTotaleElementi Trovato  false WHILE NOT (Indice=0) AND NOT Trovato DO IF UnArray[Indice]= ValoreCercato Trovato  true ELSE Indice  Indice-1

Poiché ad ogni passo della ricerca eliminiamo un elemento il massimo numero di passi è pari al numero di elementi. Indicatore:=CercaIndice(Nome, NumeroElementi,ValoreCercato); IF Indicatore<>0 THEN BEGIN write(ValoreCercato,’ è stato trovato nella posizione ‘,Indicatore:2); END ELSE write(ValoreCercato,’ non è stato trovato ‘);

FUNCTION CercaIndice(VAR Nome: NomeArray; NumeroElementi:integer; ValoreCercato:NomeStringa):integer; VAR Indice:integer; Trovato: boolean; BEGIN WHILE NOT (Indice=0) AND NOT Trovato DO IF Nome[Indice]=ValoreCercato THEN Trovato:= true ELSE Indice:= Indice-1; CercaIndice:= Indice END.

Ricerca ricorsiva Pseudo codice FUNCTION Indice (VAR UnArray; Chiave; SubRange):IType IF terminato THEN Indice  0 ELSE prendi un Candidato IF UnArray[Candidato] = Chiave THEN Indice  Candidato rivedi il SubRange riducendo le dimensioni del problema Indice  Indice(UnArray, Chiave, SubRangeRidotto) Indice  Indice(UnArray, Chiave, SubRangeIniziale) CASE BASE 1 CASE BASE 2 ESPRESSIONE RICORSIVA

Caratteristiche di una ricerca ricorsiva Gli array passati al blocco ricorsivo vengono chiamati per VAR. Una chiamata ricorsiva implica sempre una riduzione del sub range di possibili candidati. Quindi nell’intestazione è presente almeno un parametro che rappresenta il subrange di elementi. Il valore del parametro in ogni istante di computazione è funzione di un qualche subrange candidato locale. Questa espressione i cui valori devono rappresentare una riduzione del subrange candidato viene calcolata e al successivo processo di ricerca i valori vengono applicati. La condizione di terminazione è espressa in termini dei parametri del subrange candidato. Questa condizione rappresenta il CASE BASE quando non restano altri subrange candidati alla ricerca. L’altro CASE BASE si ha quando la ricerca ha buon esito, quando cioè Array[Candidate] coincide con Chiave.

RICERCA LINEARE Una funzione di Search linaeare può essere del tipo Linear(UnArray, Chiave, TotalElements) dove si presuppone che il subrange di candidati sia tra 1.. TotalElements. Esempio Supponiamo di avere un Array di record Studenti, di operare su un subrange che vada 1..NumeroStudenti. Uno dei campi del record supponiamo sia MATRICOLA. Su questo campo vogliamo fare delle ricerche. Una chiamata di funzione tipica è Indice:=Linear(Studenti; MatrCercata; NumeroStudenti) FUNCTION Linear (VAR Studenti: StudenteRecord; MatrCercata;StringaNome; NumeroStudenti :integer):integer; BEGIN IF N=0 THEN Linear:= 0 ELSE IF Studenti[N].Matricola=MatrCercata THEN Linear:=N ELSE Linear:=Linear(Studenti; MatrCercata; NumeroStudenti-1) END; CASE BASE 1 CASE BASE 2 ESPRESSIONE RICORSIVA

AnagraficaRecord = RECORD Cognome, Nome : Stringa20 END; Studente Anagrafe Nascita Matricola Cognome Nome Anno Mese Giorno AnnoCorso Risultati Media TYPE Stringa20 = STRING[20] RisultatiArray=ARRAY[1..TotaleProve] OF integer; AnagraficaRecord = RECORD Cognome, Nome : Stringa20 END; DataRecord = RECORD Giorno, Mese, Anno : integer END; StudenteRecord = RECORD Anagrafe:AnagraficaRecord; Nascita:DataRecord; Matricola:StringaNome; AnnoCorso:StringaNome; Risultati:RisultatiArray; Media:real; END;

RICERCA BINARIA Data una lista di N elementi ordinati cercare se tra essi esiste un determinato elemento. Dividiamo gli elementi ordinati in due parti. Quello che cerchiamo può appartenere o alla prima o alla seconda parte, essendo tutti gli elementi ordinati. Dividiamo la parte scelta ancora in due e applichiamo ancora il ragionamento precedente. L’algoritmo termina o quando l’ultimo elemento rimasto dalle successive suddivisioni è uguale a quello cercato, oppure quando l’intervallo rimasto non è più suddivisibile il che implica che il nostro elemento non appartiene alla lista.

Basso  1 Alto  ElementiTotali WHILE NOT (Basso<=Alto) DO Metà  (Basso+Alto) DIV 2 IF Interi[Metà ]< ValoreCercato THEN Basso  Metà+1 ELSE Alto  Metà-1 IF Alto<= ElementiTotali THEN IF Interi[Basso]=Valore Cercato THEN Indice  Basso Indice  0

Una funzione di Search binario ha bisogno di due parametri per individuare il subrange: Lo e Hi. L’indice candidato per la nuova ricerca è dato da: Mid  (Lo+Hi) DIV 2 1° CASE BASE è dato quando Lo>Hi 2° CASE BASE è dato quando l’elemento cercato è stato trovato La chiamata iniziale è del tipo Binary(UnArray, Chiave, 1, TotalElements)

Pseudo Codice FUNCTION Binary(VAR UnArray; Chiave; Lo, Hi):TipoIndice; IF Lo>Hi THEN Binary  0 ELSE Mid  (Lo+Hi) DIV 2 IF UnArray[Mid]=Chiave THEN Binary  Mid IF UnArray[Mid]<Chiave THEN Binary  Binary(UnArray, Chiave, Mid+1, Hi) Binary  Binary(UnArray, Chiave, Lo, Mid-1) Binary  Binary(UnArray, Chiave, 1, UltimoElemento)

Binary  Binary(UnArray, Chiave, 1, UltimoElemento) Esempio Supponiamo di avere un Array di record Studenti, ordinato per Matricola, di operare su un subrange che vada 1..NumeroStudenti. Una chiamata di funzione tipica è Binary  Binary(UnArray, Chiave, 1, UltimoElemento) FUNCTION Binary (VAR Studenti: StudenteRecord; MatrCercata:StringaNome; Lo, Hi :integer) :integer VAR Mid:integer; BEGIN IF Lo>Hi THEN Binary := 0 ELSE Mid  (Lo+Hi) DIV 2 IF Studenti[Mid].Matr=MatrCercata THEN Binary := Mid IF Studenti[Mid].Matr<MatrCercata THEN Binary := Binary(Studenti, MatrCercata, Mid+1, Hi) Binary := Binary(Studenti, MatrCercata, Lo, Mid-1) END; CASE BASE 1 CASE BASE 2 ESPRESSIONE RICORSIVA

Problema Scrivere una funzione ricorsiva che ritorna vero se gli elementi di UnArray di interi, con valori assegnati nel subrange 1..TotElem, sono in ordine crescente. Obiettivo ricerca un elemento che sia fuori ordine in un dato subrange. Riduciamo di volta in volta il subrange fino a quando in questo resta un solo elemento allora vuol dire che la ricerca è finita e la risposta è TRUE. Se invece si verifica che è vera l’espressione UnArray[N-1]>UnArray[N] questo significa che è stato trovato un elemento non in ordine e la funzione ritorna FALSE.

FUNCTION Ordinato(VAR UnArray:IntsArray; N:integer):boolean; BEGIN IF N=1 THEN Ordinato:= TRUE ELSE IF UnArray[N-1] > UnArray[N] THEN Ordinato:= FALSE Ordinato:=Ordinato(UnArray,N-1)

Dato un Array di interi cercare in quale posizione si trova il numero più piccolo. Iniziamo con l’ipotesi che l’ultimo elemento dell’Array sia il minimo e ricorsivamente risaliamo l’array. FUNCTION Minimo(VAR UnArray:IntsArray; Candidato,N:integer):integer; BEGIN IF N=0 THEN Minimo:= Candidato ELSE IF UnArray[N] < UnArray[Candidato] THEN Minimo:= Minimo(UnArray, N, N-1) Minimo:= Minimo(UnArray,Candidato,N-1)

Ordinamento per inserimento INSERTION SORT Ordinamento per inserimento E’ l’algoritmo più intuitivo. Se ad esempio si ha un mazzo di carte non ordinato possiamo ordinarlo scorrendo le carte una alla volta e inserendo ogni carta immediatamente dopo la carta più piccola tra quelle che la precedono. Supponiamo che il primo elemento sia già ordinato. Pseudo-codice FOR PostoSuccessivo  2 TO UltimoElemento DO sposta tutti gli elementi maggiori di Interi[PostoSuccessivo ] di un posto in avanti e metti Interi[PostoSuccessivo ] nella sua giusta posizione

Supponiamo di essere al passo j. Confrontiamo il valore dell’elemento Interi[1] UltimoElemento Tutti ordinati Non ancora ordinati 63 11 61 65 Inserisci tra questi due elementi Interi[PostoSuccessivo] 33 Algoritmo Supponiamo di essere al passo j. Confrontiamo il valore dell’elemento di Interi[j] con i suoi predecessori. Se chi lo precede ha un valore più elevato lo spostiamo di un posto in avanti. Se incontriamo nella posizione i un valore più basso allora poniamo in Interi[i+1]  Interi[j].

Pseudo-codice Temp  Interi[PostoSuccessivo] Posizione  PostoSuccessivo-1 WHILE Interi[Posizione ] > Temp DO Interi[Posizione+1]  Interi[Posizione] Posizione  Posizione -1 Potrebbe succedere che uno degli elementi sia più piccolo di tutti e che quindi noi, risalendo la lista cerchiamo di confrontarlo con l’elemento posto in Interi[0] provocando così un crash. Per evitare questo definiamo il range dell’array Interi variabile tra 0..UltimoElemento e ogni volta che scegliamo un valore per eseguire i confronti lo memorizziamo temporaneamente in Interi[0], così che se esso fosse il più piccolo di tutti comunque verrebbe posto in Interi[0+1]

PostoSuccessivo 10 19 9 30 29 12 18 [0] [1] [2] [3] [4] [5] [6] [7] 2 3 10 19 9 30 29 12 18 [0] [1] [2] [3] [4] [5] [6] [7] 9 10 19 30 29 12 18 9 10 19 30 29 12 18 4 9 10 19 30 29 12 18 [0] [1] [2] [3] [4] [5] [6] [7] 5 9 10 19 30 29 12 18 [0] [1] [2] [3] [4] [5] [6] [7] 29 9 10 19 30 12 18

N° confronti: (n-1)+(n-2)+…+1=(n-1)*n/2 9 10 19 29 30 12 18 [0] [1] [2] [3] [4] [5] [6] [7] 6 12 9 10 19 29 30 18 12 9 10 19 29 30 18 12 9 10 19 29 30 18 7 [0] [1] [2] [3] [4] [5] [6] [7] 12 9 10 19 29 30 18 18 9 10 12 19 29 30 18 9 10 12 19 29 30 18 9 10 12 19 29 30 N° confronti: (n-1)+(n-2)+…+1=(n-1)*n/2

PROCEDURE InsertionSort(VAR Interi: ArrayInteri); PostoSuccessivo, Posizione: 0..UltimoElemento; BEGIN FOR PostoSuccessivo:=2 TO UltimoElemento DO Interi[0]:=Interi[PostoSuccessivo]; Posizione:= PostoSuccessivo-1; WHILE Interi[Posizione]>Interi[0] DO Interi[Posizione+1]:= Interi[Posizione]; Posizione:=Posizione-1 END; Interi[Posizione+1]:=Interi[0] END

MERGE - SORT Un algoritmo di sort classico ha in genere una complessità di calcolo pari a O(N2). Vediamo un algoritmo che, fondato sul criterio del DIVIDE ET IMPERA ha una complessità più bassa. Il merge-sort è fondato sul principio ricorsivo di ridurre il problema di partenza di un fattore 2 e nessun processo ricorsivo attivato viene fatto più di una volta.

3 5 2 6 4 1 7 8 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 2 3 5 6 1 4 7 1 2 3 4 5 6 7 8 5 3 6 2 4 1 7 1 2 3 4 5 6 7 8

Complessità del MERGE-SORT I nodi dell’albero di sort sono log N. Per ogni nodo si fa un sort in i*N/i passi dove i rappresenta la profondità del nodo 3 5 2 6 4 1 7 8 i N° passi 1 1*N 2 2*N/2 4 4*N/4 8 8*N/8 j j*N/j …….. …….. log N log N*N/log N totale N*log N

Pseudo codice PROCEDURE SortIt(Lo,Hi:IType; VAR AnArray:ArrayType); usa i valori degli indici in input (Lo,Hi) per dividere AnArray in due subarray (inferiore e superiore) IF gli indici del subarray inferiore implicano un array ordinabile THEN SortIt(usa come indici quelli del subarray inferiore ,AnArray) IF gli indici del subarray superiore implicano un array ordinabile THEN SortIt(usa come indici quelli del subarray superiore ,AnArray) SortIt(1, TotalElement, AnArray)

Si parte con la richiesta di ordinare gli elementi dell’array compresi tra 1 e TotalEments, si riduce poi questo intervallo attraverso Lo e Hi fino a quando nel subarray non resta che un elemento. Questo è ovviamente ordinato e quindi si attiva la catena pop. Per dividere i Subarrays usiamo una variabile locale Mid. Questi Subarrays saranno prima sorted o poi merged. BASE CASE si ha quando i due subarrays sono ridotti ad una sola variabile cioè sono banalmente ordinati. Quando si arriva al Base Case allora si attiva il processo di Merge tra i due arrays adiacenti rimasti. Se invece siamo in presenza di subarrays con più di un elemento questo implica che il subarray deve essere ordinato.

7 6 3 1 2 4 5 Sort(7,8) Sort(5,6) Sort(5,8) Sort(3,4) Sort(1,2)

PROCEDURE Merge(Lo, Mid, Hi: integer; VAR AnArray:ArrayType); BEGIN END; PROCEDURE SortIt(Lo, Hi: integer; VAR AnArray:ArrayType); VAR Mid:integer; Mid:=(Lo+Hi) DIV 2; IF Lo<Mid THEN SortIt(Lo, Mid, AnArray) IF Mid+1 < Hi THEN SortIt(Mid+1, Hi, AnArray) Merge(Lo, Mid, Hi, AnArray)

Mid  (Lo+Hi) DIV 2 IF Lo < Mid THEN SortIt(Lo, Mid, AnArray) Mid  (Lo+Hi) DIV 2 IF Mid+1 < Hi THEN SortIt(Mid+1, Hi, AnArray) Lo Hi Mid 1 8 4 Lo Hi Mid+1 1 8 5 1 4 2 3 4 3 Sort(7,8) Sort(5,6) Sort(5,8) Sort(3,4) Sort(1,2) Sort(1,4) Sort(1,8) 1 2 1 5 8 6 7 8 7 5 6 5

Una maniera per verificare se la procedura SortIt funziona bene è quella di scrivere la procedura di merge, con le seguenti scritte di controllo. PROCEDURE Merge(Lo, Mid, Hi:integer; VAR AnArray:ArrayType) BEGIN write(‘Subrange del 1° Merge (‘,Lo:1,’..’,Mid:1,’)’); write(‘Subrange 2° Merge (‘,Mid+1:1,’..’,Hi:1,’)’); write(‘Subrange Ordinato (‘,Lo:1,’..’,Ni:1,’)’); END;

PROCEDURE Update(VAR AnArray:ArrayType; VAR CandidateI:integer; VAR NewElements:ElementType); BEGIN NewElement:=AnArray[CandidateI]; CandidateI:= CandidateI+1 END; PROCEDURE Merge(Lo, Mid, Hi: integer; VAR AnArray:ArrayType); VAR Temp: ArrayType; I, I1, I2 :integer; I1:=Lo; I2:=Mid+1; FOR I:=Lo TO Hi IF I1 > Mid THEN Update(AnArray, I2, Temp[I]) ELSE IF I2 > Hi THEN Update(AnArray, I1, Temp[I]) ELSE IF AnArray[I1] < AnArray[I2] THEN ELSE FOR I:=Lo TO Hi DO AnArray[I]:=Temp[I]

I1=Lo I2=Mid+1 I=Lo I1>Mid I2>Hi A(I1)<A(I2) A(I1)  T(I) Se è stata controllata tutta la prima metà allora aggiungi diret -tamente la seconda Se è stata controllata tutta la seconda metà allora aggiungi direttamente la prima Inserisci l’elemento più piccolo e incrementa opportunamente l’indice I1=Lo I2=Mid+1 I=Lo I1>Mid I2>Hi A(I1)<A(I2) A(I1)  T(I) T(I)  A(I2) I2  I2+1 I=Hi UP(A,I2,T(I)) UP(A,I1,T(I)) T(I)  A(I1) I1  I1+1 SI SI SI

2 8 21 24 13 15 17 18 1 2 3 4 5 6 7 8 I I1 I2 INCR. xx T(I) 1 5 INCR. I1 2 C C D D D I1=Lo I2=Mid+1 I=Lo I1>Mid I2>Hi A(I1)<A(I2) A(I1)  T(I) T(I)  A(I2) I2  I2+1 I=Hi UP(A,I2,T(I)) UP(A,I1,T(I)) T(I)  A(I1) I1  I1+1 B B A A B D C Merge-Sort

Svantaggi del Merge-Sort Necessita di un vettore di appoggio Effettua gli spostamenti anche se il vettore di partenza è già ordinato

Esercizio La Torre di Hanoi. Dati tre pioli verticali e n dischi di dimensioni decrescenti infilati nel primo piolo trasferire tutti i dischi sul secondo piolo, in maniera da mantenere l’ordine decrescente, dal basso verso l’alto, adoperando un terzo piolo di appoggio. Algoritmo 1 - sposta i primi n-1 dischi dal piolo 0 al piolo 2 rispettando l’ordine 2 - sposta l’ultimo disco dal piolo 0 al piolo 1 3 - sposta i primi n-1 dischi dal piolo 2 al piolo 1 rispettando l’ordine

Descrivere il processo ricorsivo implementato nel seguente programma PROGRAM TorreDiHanoi (output); TYPE piolo=0..2; VAR i, nmossa: integer; PROCEDURE muovi(disco:integer; sorgente, destinazione:piolo); BEGIN nmossa:=nmossa+1; writeln(nmossa:3, ' muovi il disco ',disco:2, ' da',sorgente:2, ' a', destinazione:2) END; PROCEDURE Hanoi(n:integer; sorgente, destinazione, ausiliario:piolo); IF n=1 THEN muovi(1,sorgente, destinazione) ELSE Hanoi(n-1,sorgente, ausiliario, destinazione); muovi(n,sorgente, destinazione); Hanoi(n-1, ausiliario, destinazione,sorgente) END FOR i:=2 TO 4 DO nmossa:=0; writeln(' ----------------------------- '); writeln(' mossa per',i:3,' dischi'); Hanoi(i,0,1,2); readln END.

FUNCTION Fattoriale (N:integer):integer; BEGIN IF N=0 THEN CONSIGLI PER LA PROGETTAZIONE DI ALGORITMI RICORSIVI 1 – Assicurarsi di aver preso in considerazione tutti i possibili base case Ad esempio nel caso del Fattoriale non avevamo tenuto conto del caso di N<0 FUNCTION Fattoriale (N:integer):integer; BEGIN IF N=0 THEN Fattoriale:=1 ELSE Fattoriale:=N*Fattoriale(N-1) END;

Un processo ricorsivo può essere diviso in 5 parti, non necessariamente tutte presenti PROCEDURE Hanoi(n:integer; sorgente, destinazione, ausiliario:piolo); BEGIN IF n=1 THEN muovi(1,sorgente, destinazione) ELSE write(‘ Prima della chiamata ‘) Hanoi(n-1,sorgente, ausiliario, destinazione); muovi(n,sorgente, destinazione); Hanoi(n-1, ausiliario, destinazione,sorgente) END END; 2. Istruzioni eseguite quando si incontra un base case Assegnano in genere valori alla soluzione banale del base case. 1. Istruzioni eseguite prima che un nuovo processo inizi Viene eseguita ogni volta che c’è la chiamata ricorsiva 4. La chiamata ricorsiva vera e propria 3. Istruzioni eseguite prima che un’altra chiamata ricorsiva venga fatta Eventualmente aggiorna i valori per la successiva chiamata 5. Istruzioni eseguite dopo la chiamata ricorsiva Eventualmente aggiorna i valori per la successiva chiamata BEGIN nmossa:=0; writeln(' ----------------------------- '); writeln(' mossa per',i:3,' dischi'); Hanoi(i,0,1,2); readln END

C(M,N)=C(M-1,N)+C(M-1,N-1) Scrivere la funzione. Esercizio La function per trovare la combinazione di M oggetti presi N alla volta può essere rappresentata dalla funzione ricorsiva C(M,0)=1 C(M,M)=1 C(M,N)=C(M-1,N)+C(M-1,N-1) Scrivere la funzione. FUNCTION Combinazioni(M,N:integer):integer; BEGIN IF M<N THEN Combinazioni:=1 ELSE IF M=N THEN ELSE Combinazioni:= Combinazioni(M-1,N) + Combinazioni(M-1,N-1) END;