Laboratorio VII: 19-Nov-2007                                         versione del 18-Nov-2007


Parte I Realizzare metodi ricorsivi - Esercizi semplici
La mia soluzione
Metodi ricorsivi
Negli esercizi per la scrittura di metodi ricorsivi che seguono, concentrarsi sulla definizione di
- caso (o casi) base
- passo ricorsivo

Se appropriato, gestire nei metodi le precondizioni , lanciando l'eccezione java.lang.IllegalArgumentException in caso di mancato rispetto delle precondizioni.

Buon lavoro.

Elencare numeri
Si scriva il metodo ricorsivo public static String recursivelyListNumbers(int n)
che restituisce una stringa contenente i numeri da 1 a n, con n > 0, in ordine crescente, separati da uno spazio.

Esempio: l'invocazione recursivelyListNumbers(5) restituisce la stringa "1 2 3 4 5"

Si programmi il metodo nella classe RecursiveNumberLister.java.
Si provi il metodo rendendo la classe eseguibile e inviando il risultato a standard output. Si passi il numero n come argomento da riga di comando.

Esempio di uso:
$java RecursiveNumberLister n
dove n e' il numero massimo da elencare.

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!
RecursiveNumberLister.java
Invertire una stringa Si scriva il metodo public static String recursivelyReverseString(String s)che inverte la stringa s passata come parametro in modo ricorsivo.

Si programmi il metodo nella classe RecursiveStringReverser.

La stringa sia passata come argomento nella riga di comando e il risultato dell'inversione visualizzato a standard output (o mediante la classe JOptionPane).
Esempio: l'inverso della stringa "Roma" e' la stringa "amoR".

Esempio di uso:
$java RecursiveStringReverser string
RecursiveStringReverser.java
Ricercare il  minimo in un array di interi
Si scriva la classe eseguibile RecursiveMinFinder capace di trovare il minimo in un array di numeri interi riempito solo in parte.
Nella classe si programmi il metodo ricorsivo privato
   private static int recursivelyFindMin(int[] a, int aSize)
dove int[] a e' l'array nel quale si effettua la ricerca e int aSize il numero di elementi effettivamente inseriti nell'array.

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!

Si renda la classe eseguibile generando un array di numeri interi casuali, compresi fra 1 e n, estremi compresi, di dimensione dim .
I valori di n e dim siano passati come argomenti nella riga di comando.

Nota:
per la generazione dei numeri casuali si usi il metodo int nextInt(int k) della classe java.util.Random.

Si calcoli il minimo dell'array e si visualizzi il risultato in una finestra di dialogo (con la classe javax.swing.JOptionPane) in un messaggio con un formato simile al seguente, ottenuto per dim = 7 e n = 999:



Esempio di uso:
$java RecursiveMinFinder 7 999

Gestione delle precondizioni nel metodo recursivelyFindMin()
Nei metodi privati, in genere, non si gestiscono precondizioni, dal momento che sono usati solo all'interno della classe.
Si presuppone, quindi, che il programmatore faccia attenzione a invocare il metodo con valori corretti dei parametri .
I metodi pubblici possono essere invocati, invece, da altre classi. Si rendono, per questo, robusti tramite la verifica delle precondizioni, attraverso il lancio di eccezioni o, in alternativa, l'enunciato assert.
Lanciare l'eccezione java.lang.IllegalArgumentException se l'array non ha almeno un elemento.
RecursiveMinFinder.java
Calcolare la media degli elementi di un array
Si scriva la classe eseguibile RecursiveMeanCalculator che calcola la media degli elementi di un array di interi.

Nella classe si programmino i metodi ricorsivi privati
   private static double recursivelyComputeMean(int[] a, int aSize)
   private static double iterativelyComputeMean(int[] a, int aSize)
che calcolano la media degli elementi dell'array int[] a riempito solo in parte, il primo in modo ricorsivo, il secondo in modo iterativo. Sia int aSize il numero di elementi inseriti nell'array.

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!
RecursiveMeanComputer.java

Parte II Realizzare metodi ricorsivi - Esercizi piu' complessi
La mia soluzione
Elencare numeri dieci per riga in colonne larghe 5 caratteri.
Si modifichi la classe RecursiveNumberLister, in modo che i numeri compaiano in righe di 10 numeri ciascuna e che ciascun numero sia espresso con 5 caratteri, eventualmente usando spazi a sinistra del numero:
Esempio:
    1     2     3     4     5     6     7     8     9    10
   11    12    13    14    15    16    17    18    19    20
   21    22    23    24    25    26    27    28    29    30

Suggerimento: andare a capo quando n - 1 e' divisibile per il numero di colonne, dove n e' il parametro della chiamata ricorsiva.
InColumnRecursiveNumberLister.java
Fattorizzare un numero intero non negativo
Si scriva il metodo ricorsivo public static String recursivelyMakeFactors(int n)
che calcola la fattorizzazione di un numero intero non negativo in modo ricorsivo.

Esempio: l'invocazione recursivelyMakeFactors(12) restituisce la stringa "2*2*3"

Si programmi il metodo nella classe RecursiveFactorsComputer.

Si renda la classe eseguibile e a si provi passando il numero da fattorizzare da riga di comando.
Esempio di uso:
$java RecursiveFactorsComputer n

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!
RecursiveFactorsComputer.java
Generare l'insieme delle sottostringhe
Si scriva la classe eseguibile RecursiveSubstringGenerator capace di generare tutte le sottostringhe di una stringa. La stringa sia passata come argomento nella riga di comando e l'insieme delle sottostringhe visualizzato a standard output.
L'insieme delle sottostringhe della stringa "abc" e'  il seguente: {a, ab, abc, b, bc, c}.

Esempio di uso:
$java RecursiveSubstringGenerator string

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!
RecursiveSubstringGenerator.java
Ricercare  una sottostringa in una stringa data
Si scriva la classe eseguibile RecursiveSubstringFinder capace di trovare una sottostringa in una stringa data.
Esempio:
la sottostringa oTo e' contenuta nella stringa PlutoTopolino nei caratteri di indice da 4 a 6.

Nella classe si programmi il metodo ricorsivo
   public static boolean recursivelyFindSubstring(String s, String sub)
dove String s  e String sub sono la sottostringa in cui effettuare la ricerca e la stringa da trovare, rispettivamente.

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!

Si renda la classe eseguibile, realizzando le seguenti due opzioni:
- opzione "/d": la stringa e la sottostringa sono passate come argomenti nella riga di comando
   Esempio di uso: $java RecursiveSubstringFinder /d PlutoTopolino oTo
 
- opzione "/r": stringhe di caratteri alfabetici casuali generate internamente al programma
   le dimensioni delle due stringhe  sono passate come argomenti sulla riga di comando
   Esempio di uso: $java RecursiveSubstringFinder /r 100 2

Se la sottostringa e' presente nella stringa, si invii un testo in una finestra di messaggi con il formato simile a quello indicato di seguito:


 
Suggerimento:
per calcolare l'indice definire una variabile statica di classe, da incrementare a ogni chiamata ricorsiva. L'indice nella stringa corrisponde infatti al livello di annidamento delle chiamate ricorsive: la prima chiamata ricorsiva verifica la sottostringa dal carattere di indice 0, la seconda dal carattere di indice 1 e cosi' di seguito.
RecursiveSubstringFinder.java
Calcolo ricorsivo dei numeri primi
Scrivere il metodo ricorsivo
public static int[] recursiveListPrimeNumbers(int n)
che restituisce un array di interi contenente i numeri primi da 1 a n, con n >= 1, estremi compresi.

Programmare il metodo nella classe eseguibile RecursivePrimeNumberLister che riceve il numero n come argomento sulla riga di comando.

Algoritmo ricorsivo: da leggere solo se non riuscite a definirlo da soli!
RecursivePrimeNumberLister.java

Parte III
Linguaggio Java: classi, ordinamento, ricerca, generazione di numeri casuali
La mia soluzione
Ordinare e ricercare numeri
Scrivere la classe ArrayOrdinato la cui interfaccia pubblica è documentata in ArrayOrdinato.html, in grado di memorizzare numeri interi, mantenendo l'insieme ordinato.
Scrivere successivamente la classe OrdinatoreNumerico che:
   1. genera N numeri casuali fra 1 e N compresi, dove N e' passato come argomento nella riga di comando, memorizzandoli in un ArrayOrdinato.
   2. calcola la media dei valori dell'ArrayOrdinato e la invia a standard output
   3. esegue un ciclo: (uscire dal ciclo quando la stringa acquisita è null o la stringa nulla "")
      - acquisizione da standard input di un numero i fra 1 e N
      - ricerca del numero i nell'ArrayOrdinato
      - invio di un messaggio a standard output del tipo
        "k = i: non presente" oppure
        "k = i: indice j"
   4. invii a standard output in ordine decrescente i valori dell'ArrayOrdinato, 10 numeri per riga in colonne  
       equispaziate. Si usi successivamente con re-indirizzamento dell'input standard.

Esempio di uso:
$java OrdinatoreNumerico dimensione < nomefile

ArrayOrdinato.java
OrdinatoreNumerico.java

Misurare le prestazioni degli algoritmi di ordinamento.
Organizzare delle misure per calcolare il tempo impiegato dall'elaboratore a ordinare un insieme di numeri interi, usando i tre algoritmi di ordinamento studiati: SelectionSort, MergeSort e InsertionSort.

Allo scopo, codificare le classi Cronometer.java, RandomSequencesGenerator.java e ArrayAlgorithms.java con le seguenti interfacce pubbliche: Cronometer.html, RandomSequencesGenerator.html e ArrayAlgorithms.html.
La classe Cronometer.java realizza un cronometro per misurare il tempo.
La classe RandomSequencesGenerator.java e' in grado di generare sequenze di numeri pseudo-casuali, mentre la classe
ArrayAlgorithms.java contiene la codifica degli algoritmi di ordinamento e di ricerca.

Risoluzione del tempo nell'elaboratore.
Un normale orologio da polso ha una risoluzione pari a 1 secondo. L'aggiornamento del tempo avviene, cioe', ogni secondo. Ne consegue che due diverse misure del tempo differiranno di almeno un secondo.
Qual e' la risoluzione del tempo che possiamo misurare in java usando il metodo System.currentTimeMillis()?
Il tempo nell'elaboratore e' aggiornato dal sistema operativo. Il metodo currentTimeMillis() non fa altro che interrogare l'orologio dell'elaboratore.
Qual e' la risoluzione dell'orologio dell'elaboratore?
Dipende dal sistema operativo, ma tipicamente e' di 10 ms, o multipli di questo intervallo. Per sapere la risoluzione dell'elaboratore sul quale si effettua la misura, si esegua la seguente classe: TimeResolutionTester.java.

Per calcolare il tempo necessario a ordinare un  array, generiamo un array di numeri interi casuali mediante la classe RandomSequencesGenerator. La dimensione n dell'array sia  passata come argomento da riga di comando. Per fare una misura non troppo errata, dobbiamo misurare tempi molto superiori alla risoluzione del tempo nell'elaboratore.
Usiamo la tecnica di ordinare molte volte un array (k volte, con k molto maggiore di 1). Misuriamo il tempo totale necessario tTot a ordinare k volte l'array  e calcoliamo il tempo per ordinare una sola volta l'array come tTot/k.
Poiche' l'algoritmo di ordinamento insertionSort dipende dalla distribuzione dei numeri nell'array, prima di ordinare l'array si deve ricopiare l'array non ordinato. Si faccia per tutti gli algoritmi e si sottragga il tempo necessario a tale operazione al tempo misurato per gli algoritmi di ordinamento.

La classe SortCronometer.java, preparata dal docente, esegue la misura e invia i dati a standard output sotto forma di tabella.
Si esegua la classe, leggendo i dati con re-indirizzamento dello standard input sul file sortCronometer.txt.
(pazientate, perche'  l'esecuzione impiega molti secondi!)

Esempio d'uso:
$java SortCronometer < sortCronometer.txt

Si osservino attentamente i risultati: sono coerenti con l'analisi teorica?

Nota:
Il sistema di misura approntato e' ingenuo e non tiene conto di alcuni fenomeni:
1.  il sistema operativo e' multi-processo e quindi, la misura potrebbe essere falsata da altri oprogrammi in esecuzione. Si chiudano tutti i programmi, eccetto un terminale, prima di effettuare la misura. Il sistema operativo effettua comunque varie attività, che possono falsare le misure.
2.  il Garbage Collector puo' alterare la misura.
Cronometer.java
RandomSequencesGenerator.java
ArrayAlgorithms.java
SortCronometer.java

Parte IV
Consultare la documentazione di java
Collegamenti
javaDocs
Nella documentazione di java in linea presso l'aula Taliercio consultare la documentazione relativa ai seguenti componenti della libreria standard:
- classe java.io.Writer: che funzioni svolgono i metodi
   void flush(), void close()?
- classe  java.util.Arrays: che algoritmo usa il metodo
 void sort(int[] a),
 che funzione esegue il metodo
 void fill(int[] a, int val)?


Saper consultare rapidamente la documentazione java in linea nel sito dell'Aula Didattica Taliercio e' molto importante. Infatti in sede di prova pratica di esame, questa sarà la sola documentazione disponibile.
Il link trovatelo da soli