Problemi di verifica universale - Dipartimento di Informatica e
Transcript
Problemi di verifica universale - Dipartimento di Informatica e
Corso di Laurea Ingegneria Civile Fondamenti di Informatica Dispensa 18 Iterazione C. Limongelli Novembre 2008 Iterazione 1 Contenuti Si vogliono formalizzare diverse tecniche per la risoluzione di problemi di: Accumulazione Conteggio Verifica esistenziale Verifica universale Ricerca Iterazione 2 Accumulazione Calcolare una proprietà sintetica (cumulata) da una sequenza di valori l i calcolo della somma di una sequenza di valori numerici calcolo del fattoriale di un numero naturale Tecnica dell’accumulazione una variabile i bil di accumulazione l i (accumulatore) ( l t ) per calcolare l l l’informazione cumulativa una operazione p di accumulazione,, binaria,, che deve essere applicata all’accumulatore e a ciascun elemento della sequenza inizialmente all’accumulatore viene assegnato l’elemento neutro dell’operazione dell operazione di accumulazione Iterazione 3 Accumulazione: esempi p Somma della sequenza di n numeri Calcolo del fattoriale di un numero Iterazione 4 Somma degli g elementi di una sequenza q int numero; // elemento corrente della sequenza int somma; // somma degli elementi della sequenza /* leggi una sequenza di numeri interi e * calcolane la somma */ /* inizialmente somma vale zero */ somma = 0; /* fi finché hé ci i sono altri lt i elementi l ti nella ll sequenza, * leggili e sommali a somma */ while (!Lettore.in.eoln()) { // finché ci sono // altri elementi /* leggi un elemento della sequenza */ / / numero = Lettore.in.leggiInt(); /* incrementa somma di numero */ somma = somma + numero; } / visualizza somma */ /* / System.out.println(somma); sequenza: letta dalla tastiera accumulatore: somma operazione di accumulazione: + elemento neutro dell’operazione dell operazione di accumulazione: 0 Iterazione 5 Uso dell’accumulatore Si consideri la variabile somma nel calcolo della somma di una sequenza di numeri l’obiettivo della variabile somma è quello di memorizzare la somma di tutti gli elementi letti questo obiettivo viene raggiunto solo dopo aver letto tutti gli elementi della sequenza più precisamente prima dell’esecuzione dell’istruzione ripetitiva somma vale zero – è uguale alla somma di tutti gli elementi letti fino a quel momento dopo d ciascuna i esecuzione i del d l corpo dell’istruzione d ll’i t i ripetitiva, i titi somma vale la somma di tutti gli elementi letti fino a quel momento in generale somma è uguale alla somma della porzione di sequenza letta ed elaborata fino a quel momento Iterazione 6 Accumulazione: schema risolutivo ... dichiarazione della variabile accumulatore ... accumulatore = elemento neutro dell’operazione di accumulazione ; ... altre lt i inizializzazioni i i li i i ... per ciascun elemento della sequenza { ... accedi al prossimo elemento ... accumulatore = applica l’operazione di accumulazione all’accumulatore e all’elemento corrente ; } ... altre elaborazioni ... usare un nome opportuno per l’accumulatore Iterazione 7 Calcolo di una sottostringa… g Scrivere un metodo di classe String sottostringa(String s, int inizio, int fine) che calcola la sottostringa di s che consiste dei caratteri compresi tra quello di posizione inizio e quello di posizione fine-1 • si comporta come s.substring(inizio, fine) • ad esempio, sottostringa("automobile", 2, 6) deve restituire "tomo" usa solo le seguenti operazioni sulle stringhe • int length() g () • char charAt() • + (concatenazione) Iterazione 8 …Calcolo di una sottostringa… g È un problema di accumulazione sequenza • la sequenza q dei caratteri di s tra le p posizioni inizio ((compresa) p )e fine (esclusa) accumulatore • una stringa, ss operazione operazione di accumulazione • la concatenazione + elemento neutro dell’operazione di accumulazione • la stringa vuota "" Iterazione 9 …Calcolo di una sottostringa g /* Calcola la sottostringa di s compresa tra i * caratteri di posizione da inizio a fine-1. */ public static String sottostringa(String s, int inizio, int fine) { // pre: s!=null && // inizio>=0 && inizio<=s inizio<=s.length() length() && // fine>=inizio && fine<=s.length() String ss; // la sottostringa di s tra inizio e fine int i; // indice per la scansione di s /* * * ss calcola la sottostringa, come concatenazione dei caratteri tra la posizione inizio (inclusa) e fine (esclusa) */ = ""; // la stringa vuota è l'elemento neutro // della concatenazione for ( (i=inizio; i<fine; i++) ) ss = ss + s.charAt(i); // concatenazione a destra // e conversione automatica return ss; } Iterazione 10 Esercizio Scrivere un metodo inversa che ha come parametro una stringa S e che calcola la stringa ottenuta da S invertendone l’ordine dei caratteri ad esempio, inversa("automobile") deve restituire "elibomotua" elibomotua Iterazione 11 Conteggio gg I problemi di conteggio sono un caso particolare ti l d deii problemi bl i di accumulazione l i • l’accumulatore è un contatore • l’operazione l’ i di accumulazione l i è un incremento i t unitario it i – vedi di oggetto Contatore • l’aggiornamento gg deve essere eseguito g sempre (conteggio), ( gg ) oppure condizionatamente (conteggio condizionale) al soddisfacimento di una proprietà da parte dell’elemento corrente della sequenza • ad esempio – leggi una sequenza di numeri e calcola la sua lunghezza – conteggio – leggi l i una sequenza di numerii e calcola l l il numero degli d li elementi l ti che h valgono l zero – conteggio condizionale – calcola il numero di occorrenze di un carattere alfabetico in una stringa – conteggio condizionale Iterazione 12 Conteggio: gg schema risolutivo int contaProprietà; // numero di elementi che // soddisfano la proprietà contaProprietà = 0; ... altre inizializzazioni ... per ciascun elemento della sequenza { ... accedi di al l prossimo i elemento l t ... if (l’elemento corrente soddisfa la proprietà) contaProprietà++; } ... altre elaborazioni ... usare un nome opportuno per il contatore Iterazione 13 Esercizio Scrivere una applicazione che legge dalla tastiera una sequenza di numeri interi e ne conta e visualizza, separatamente, il numero degli elementi positivi e il numero degli elementi negativi (gli zeri non vanno contati) Scrivi una sequenza di numeri interi 10 20 0 -10 10 4 -8 8 La sequenza contiene 3 elementi positivi e 2 negativi Iterazione 14 Verifica esistenziale Bisogna determinare se una sequenza di elementi contiene almeno un elemento che soddisfa una certa proprietà in altre parole: p si vuole verificare che in una sequenza di elementi a1,…, an esiste almeno un elemento che verifica una data proprietà p cioè che ∃ i ∈{1,..,n}, ∈{1 n} p(ai) = true Iterazione 15 Verifica esistenziale: schema risolutivo … Viene usata una variabile booleana che indica se la sequenza contiene almeno un elemento che soddisfa la proprietà Inizialmente si assegna alla variabile booleana un valore che indica convenzionalmente che la sequenza non contiene nessun elemento che soddisfa la proprietà (false) A partire dal primo elemento della sequenza si verifica se l’elemento corrente soddisfa la proprietà • se l’elemento corrente soddisfa la proprietà, allora si assegna alla variabile booleana un valore che indica convenzionalmente che la sequenza contiene almeno un elemento che soddisfa la p proprietà p (true) ( ) Quando si trova un elemento che soddisfa la proprietà ci si ferma (non ha senso esaminare oltre perché il problema è risolto) Iterazione 16 … Verifica esistenziale: schema risolutivo boolean proprietaSoddisfatta; // almeno un elemento // soddisfa ddi f l la proprietà i tà proprietaSoddisfatta = false; ... altre inizializzazioni ... finche’ non trovo un elemento che soddisfa la proprieta’ while (!proprietaSoddisfatta && sequenza non terminata) { ... accedi al prossimo elemento ... if (l’elemento corrente soddisfa la proprietà) proprietaSoddisfatta = true; } ... altre elaborazioni ... usare un nome opportuno per la variabile booleana Iterazione 17 Un errore comune Un errore comune nella verifica esistenziale • ri-assegnare alla variabile booleana usata per la verifica esistenziale il valore che gli è stato assegnato nella sua inizializzazione contieneZero = false; while hil (!Lettore.in.eoln()&& (!L tt i l ()&& ! !contieneZero) ti Z ) { /* legge il prossimo elemento della sequenza */ numero = Lettore.in.leggiInt(); /* se numero vale zero, allora la sequenza * contiene almeno uno zero */ / if (numero==0) contieneZero = true; else contieneZero = false; } • Se nella condizione del while non compare !contieneZero il programma verifica se l’ultimo elemento della sequenza letta vale zero • Se nella condizione del while compare !contieneZero l’ultimo else e’ inutile Iterazione 18 Verifica se una sequenza contiene almeno uno zero ... legge una sequenza di numeri interi e verifica se contiene almeno uno zero ... int numero; // elemento corrente della sequenza boolean contieneZero; // la sequenza contiene almeno // un elemento uguale a zero ... visualizza un messaggio ... /* legge la sequenza e verifica * se contiene almeno uno zero */ / contieneZero = false; while (!Lettore.in.eoln()) { /* legge il prossimo elemento della sequenza */ numero = Lettore.in.leggiInt(); () /* se numero vale zero, allora la sequenza * contiene almeno uno zero */ if (numero==0) (numero 0) contieneZero = true; } ... il risultato è contieneZero ... Iterazione 19 Verifica e ca u universale e sa e Un problema di verifica universale consiste nel verificare se tutti gli elementi di una sequenza a1,…, an soddisfano una certa proprietà p • una variante (duale) dei problemi di verifica esistenziale In altre parole: Un p problema di verifica universale è soddisfatto,, se tutti gli g elementi verificano una data proprietà p: ∀ i ∈ {1,..,n}, p(ai) = true Oppure un problema di verifica universale non è soddisfatto se esiste almeno un elemento che non verifica p: ∃ i ∈ {1,..,n}, {1 n} p(ai) = false La verifica universale si può sempre ricondurre alla ll verifica ifi esistenziale i t i l Iterazione 20 Verifica universale: schema risolutivo Un problema di verifica universale può essere sempre ricondotto a un problema di verifica esistenziale il problema diventa quello di verificare se non esiste nessun elemento della sequenza che non soddisfa la proprietà inizialmente si assegna alla variabile booleana un valore che indica convenzionalmente che tutti gli elementi della sequenza soddisfano la proprietà (true) per ognii elemento l t d della ll sequenza, sii verifica ifi se l’elemento corrente non soddisfa la proprietà • se l’elemento corrente non soddisfa la p proprietà, p allora si assegna alla variabile booleana un valore che indica convenzionalmente che non tutti gli elementi della sequenza soddisfano la proprietà (false) Iterazione 21 Verifica universale:schema risolutivo boolean proprietaSoddisfatta; /* assumo che tutti gli elementi soddisfano la proprieta’ */ proprietaSoddisfatta = true; ... altre inizializzazioni ... finche non trovo un elemento che non soddisfa finche’ la proprieta’ while (proprietaSoddisfatta && la sequenza non e’ finita){ ... accedi di al l prossimo i elemento l t ... if (l’elemento corrente non soddisfa la proprietà) proprietàSoddisfatta p p = false; } ... altre elaborazioni ... usare un nome opportuno per la variabile booleana Iterazione 22 Verifica se una sequenza di dieci elementi è crescente ... legge una sequenza di dieci numeri interi e verifica se è crescente ... i t numero; int // elemento l t corrente t d della ll sequenza int precedente; // elemento che precede numero // nella sequenza int i; // per contare i numeri letti boolean crescente; // la sequenza è crescente /* il primo elemento della sequenza è il * precedente del prossimo che sarà letto */ precedente = Lettore.in.leggiInt(); /* finora la sequenza letta è crescente */ crescente = true; i=0; gg e elabora g gli altri elementi della sequenza q */ /* legge while(i<10 && crescente) { /* legge il prossimo numero e verifica che * la sequenza sia ancora crescente */ gg (); numero = Lettore.in.leggiInt(); if (precedente>=numero) crescente = false; i++; /* prepara la prossima iterazione */ precedente = numero; } ... il risultato della verifica è crescente ... Iterazione 23 Verifica l’uguaglianza g g tra stringhe g Scrivere un metodo boolean uguali(String s, String t) che verifica se le stringhe s e t sono uguali si comporta come s.equals(t) • ad esempio, uguali("alfa", "alfa") deve restituire true, mentre uguali("alfa", li(" lf " "beta") "b t ") deve d restituire tit i false f l due stringhe s e t sono uguali se • s e t hanno la stessa lunghezza • ciascun carattere della stringa s è uguale al carattere della stringa t che occupa la stessa posizione Iterazione 24 Uguaglianza g g tra stringhe g Algoritmo: • continua il confronto tra le stringhe solo se finora non sono state riscontrate differenze / verifica se s e t sono uguali */ /* / if (s.length()==t.length()) { /* s e t hanno la stessa lunghezza: s e t /* p possono essere uguali, g , ma sono diverse * se contengono almeno un carattere diverso */ uguali = true; i = 0; while (uguali && i<s.length()) { if (s.charAt(i) != t.charAt(i)) uguali = false; i = i+1; } else /*s e t hanno lunghezza diversa, quindi sono diverse */ uguali li = f false; l Iterazione 25 Verifica esistenziale e verifica universale Verifica esistenziale: Si vuole verificare che in una sequenza di elementi a1,…, an esiste almeno un elemento che verifica una data proprietà p cioè che ∃ i ∈{1,..,n}, p(ai) = true Verifica universale: Si vuole verificare che in una sequenza di elementi a1,…, an tutti gli elementi della sequenza verificano una data proprietà p cioè i è che h ∀ i ∈ {1,..,n}, p(ai) = true Oppure si può verificare che esiste almeno un elemento che non verifica la proprietà p: in tal caso la verifica universale non è soddisfatta ∃ i ∈ {1,..,n}, {1 } p(a ( i) = false f l Iterazione 26 Esercizi Scrivere un metodo boolean primo(int n) che verifica se il numero naturale t l n è primo i • ad esempio, primo(8) deve restituire false Scrivere un metodo boolean caratteriUguali(String s) che verifica se i caratteri di s sono tra loro tutti uguali • ad d esempio, i caratteriUguali("aaa") tt iU li(" ") deve d restituire tit i true t Scrivere un metodo boolean prefisso(String s, String t) che verifica se la stringa t è un prefisso della stringa s • ad esempio, prefisso("alfabeto", "alfa") deve restituire true Scrivere un metodo boolean suffisso(String s, String t) che verifica se la stringa t è un suffisso della stringa s • ad esempio, suffisso("aereo", "reo") deve restituire true Iterazione 27 Esercizi Scrivere un metodo boolean palindroma(String s) che verifica se la stringa s è palindroma • ad esempio, palindroma("abcba") deve restituire true Scrivere Sc e e u un metodo etodo boolean anagramma(String s, String t) che verifica se le stringhe s e t sono tra loro degli anagrammi • ad esempio, esempio anagramma("carpa", anagramma("carpa" "capra") deve restituire true Iterazione 28 Ricerca I problemi di ricerca sono un caso più generale dei problemi di verifica esistenziale bisogna verificare se una sequenza contiene almeno un elemento che soddisfa una certa proprietà in caso affermativo, bisogna calcolare delle ulteriori informazioni circa uno degli elementi che soddisfa la proprietà esempio • ricerca di un numero telefonico in un elenco telefonico a partire dal cognome e nome di un utente Iterazione 29 Indice di un carattere in una stringa g Scrivere un metodo int posizioneDi(String s, char car) che verifica verifica se la stringa s contiene almeno un carattere uguale a car restituisce la p posizione della p prima occorrenza del carattere car nella stringa s • oppure il valore -1 – se s non contiene nessun carattere uguale a car si comporta come s.indexOf(car) • ad esempio, posizioneDi("alfa", 'f') deve restituire 2, posizioneDi("alfa" posizioneDi( alfa , 'a') a ) deve restituire 0, 0 mentre posizioneDi( posizioneDi("alfa" alfa , 'b') deve restituire -1 Iterazione 30 Indice di un carattere in una stringa g /* Verifica se la stringa s contiene il carattere car, * restituisce la posizione della prima * occorrenza di car i in s, oppure -1. 1 */ public static int posizioneDi(String s, char car) { // pre: s!=null int posizione; // posizione di car in s int i; // indice per la scansione di s / scandisce i caratteri di s alla ricerca della /* * prima occorrenza di car in s */ /* finora non è stata trovato nessun car in s */ posizione = -1; i 0 i=0; while (posizione==-1 && i<s.length()) { if (s.charAt(i)==car) /* è stato trovato car in posizione i */ posizione = i; i = i+1; } return posizione; } Iterazione 31 Indice di un carattere in una stringa g /* finora non è stata trovato nessun car in s */ posizione = -1; i = 0; while(posizione==-1 && i<s.length()) { if (s.charAt(i)==car) ( ( ) ) /* è stato trovato car in posizione i */ posizione = i; i++; } La variabile posizione viene utilizzata come segue • posizione rappresenta la posizione della prima occorrenza di car in s • p posizione vale -1 se s non contiene nessuna occorrenza del carattere car – il valore -1 denota un “posizione non valida” s • inizialmente posizione vale -1 e, scandendo i caratteri di s, se ne viene trovato uno uguale a car, car la posizione di questo carattere viene assegnata a posizione Iterazione 32 Ricerca: schema risolutivo ... info; // informazione associata // all’elemento cercato info = informazione non trovata ; ... altre inizializzazioni ... finche’ non trovo un elemento della sequenza che soddisfa la proprieta’ { ... accedi al prossimo elemento ... if (l’elemento corrente soddisfa la proprietà) info = informazione associata all’elemento corrente ; } ... altre elaborazioni ... Iterazione 33 Indice di una sottostringa g in una stringa g Scrivere un metodo int posizioneDi(String s, String t) che • verifica se la stringa g s contiene almeno una sottostringa g uguale g alla stringa t • restituisce la posizione in cui inizia la prima sottostringa di s uguale a t – oppure -1 se s non contiene nessuna sottostringa uguale a t • sii comporta come s.indexOf(t) i d Of( ) – ad esempio, posizioneDi("sottostringa", "otto") deve restituire 1, posizioneDi("mamma", p ( , "ma")) deve restituire 0,, mentre posizioneDi("mamma", "pa") deve restituire -1 Iterazione 34 Indice di una sottostringa g in una stringa g /* Verifica se la stringa s contiene la sottostringa t * e restituisce la posizione della prima * occorrenza di t in s, oppure -1. */ public static int posizioneDi(String s, String t) { // pre: s!=null && t!=null i int posizione; i i // posizione i i d della ll sottostringa i t i in s int ls, lt; // lunghezza di s e lunghezza di t int i; // indice per la scansione // d delle ll sottostringhe tt t i h di s int j; // indice per la scansione di t boolean ssug; // la sottostringa di s che inizia in // i ed ha lunghezza lt è uguale a t /* inizializzazioni */ ls = s s.length(); length(); lt = t.length(); ... segue ... Iterazione 35 Indice di una sottostringa g in una stringa g /* scandisce le sottostringhe di s * alla ricerca di una sottostringa uguale a t */ / posizione = -1; for (i=0; posizione==-1 && i<=ls-lt; i++) { /* scandisce i caratteri di t per verificare * se l la sottostringa tt t i di s i iniziante i i t * in posizione i e lunga lt è uguale a t */ ssug = true; for (j=0; (j 0; ssug && j<lt; j++) { if (s.charAt(i+j) != t.charAt(j)) ssug = false; } if (ssug) ( ) // sottostringa di s uguale a t trovata posizione = i; // allora t è sottostringa di s } return posizione; } Iterazione 36 Un’altra soluzione public static int posizioneDi(String s, String t) { // pre: s!=null && t!=null int posizione; // posizione della sottostringa t in s int ls, lt; // lunghezza di s e lunghezza di t int i; // indice per la scansione // delle sottostringhe di s /* inizializzazioni */ ls = s.length(); lt = t.length(); /* scandisce le sottostringhe di s alla ricerca di * una sottostringa uguale a t */ posizione = -1; for (i=0; posizione==-1 && i<=ls-lt; i++) { if ( uguali(s,i,i+lt,t) g ( , , , ) ) posizione = i; } return posizione; } Iterazione 37 Esercizi Scrivere un metodo int parole(String s) che calcola il numero di parole che compaiono in s • per parola si intende una sequenza massimale di caratteri contigui che non contiene nessuno spazio bianco • ad esempio, parole("la mia casa") deve restituire 3, mentre parole(" l (" uno,due.tre d t ") deve d restituire tit i 1 Scrivere Sc e e u un metodo etodo St String g tagliaSpazi(String tag aSpa (St g s) che calcola e restituisce i caratteri di s ad eccezione degli eventuali spazi bianchi iniziali e finali • ad esempio, esempio tagliaSpazi(" a b c ") deve restituire "a b c" Iterazione 38 Riepilogo p g della dispensa p Problemi di accumulazione Cosa sono: calcolano un singolo risultato da una sequenza di dati Come si risolvono: si usa una variabile (accumulatore) e una operazione (operazione di accumulazione) che per ognii elemento l t d della ll sequenza viene i applicata li t all valore l attuale dell’accumulatore e all’elemento della sequenza per ottenere il nuovo valore dell’accumulatore dell accumulatore. All’accumulatore all’inizio va assegnato l’elemento neutro dell’operazione di accumulazione. Esempio: somma degli elementi di una sequenza 39 Riepilogo p g della dispensa p Problemi di conteggio Cosa sono: contano quanti sono gli elementi di una sequenza o contano gli elementi di una sequenza che soddisfano una certa proprietà (conteggio condizionato). Sono un caso particolare dei problemi di accumulazione, in cui l’operazione di accumulazione è l’incremento unitario. unitario Come si risolvono: si usa una variabile (contatore) cui si assegna inizialmente valore zero e che si incrementa di uno quando si esamina un elemento della sequenza (conteggio) o quando l’elemento della sequenza soddisfa la proprietà di interesse (conteggio condizionale) Esempi: leggi una sequenza di numeri e calcola la sua lunghezza (conteggio); leggi una stringa e calcola il numero di caratteri maiuscoli (conteggio condizionale) 40 Riepilogo p g della dispensa p Problemi di verifica esistenziale Cosa sono: problemi in cui si vuole determinare se una sequenza di elementi contiene almeno un elemento che soddisfa una certa proprietà Come si risolvono: si usa una variabile booleana a cui si assegna inizialmente valore false (si assume che la sequenza non contenga elementi che soddisfano la proprietà). Si esaminano gli elementi della sequenza e appena si trova un elemento che soddisfa la proprietà si assegna valore true alla variabile booleana e ci si ferma. Se nessun elemento soddisfa la proprietà cercata la variabile booleana rimarrà a false false. Esempi: legge una sequenza di numeri interi e verifica se contiene almeno uno zero 41 Riepilogo p g della dispensa p Problemi di verifica universale Cosa sono: problemi in cui si vuole determinare se tutti gli elementi di una sequenza soddisfano una certa proprietà. Si possono sempre ricondurre a problemi di verifica esistenziale (esiste un elemento che non soddisfa la proprietà? No = tutti gli elementi soddisfano la proprietà) Come si risolvono: si usa una variabile booleana a cui si assegna inizialmente valore true (si assume che tutti gli elementi della sequenza soddisfano la proprietà). Si esaminano gli elementi della sequenza e appena si trova un elemento che non soddisfa la proprietà si assegna valore false alla variabile booleana e ci si ferma ferma. Se tutti gli elementi soddisfano la proprietà cercata la variabile booleana rimarrà a true. Esempi: data una stringa verifica se contiene solo cifre 42 Riepilogo p g della dispensa p Problemi di ricerca Cosa sono: sono un’estensione un estensione dei problemi di verifica esistenziale: non voglio solo sapere se esiste un elemento che soddisfa una certa proprietà, ma voglio anche sapere quale Come si risolvono: si usa una variabile che conterrà ll’informazione informazione cercata e le si assegna inizialmente un valore non valido (es. -1 per la posizione di un carattere in una stringa). Si esaminano gli elementi della sequenza e appena si trova un elemento che soddisfa la proprietà si assegna il valore cercato alla variabile. Se l’elemento cercato non è presente nella sequenza la variabile resterà al valore non valido datole inizialmente. Esempi: trova il primo carattere maiuscolo della stringa 43 Riferimenti al libro di testo Per lo studio di questi argomenti si fa riferimento al libro di testo, e in particolare al Capitolo p 17 Escluso il paragrafo 17.7 Definizione di metodi 44