Soluzione Esercizio 10 dell'Esercitazione #2

Esercizio 10

Obiettivo: Approfondimenti su alcuni problemi e trucchi ricorrenti nella programmazione.

Attività:

Riprendiamo l'esercizio sulla variabile turno e vediamo un po' di variazioni sul tema... Mi raccomando: provate TUTTE quelle che vi sembrano soluzioni scrivendo, compilando ed eseguendo un programma. Spesso quello che sulla carta sembra corretto, poi non lo è...

  1. (*) E se si vuole che turno assuma ciclicamente i valori 1, 2, 3 e 4? Suggerimenti: Ripartite dalla versione originale e provate a invertire l'ordine delle operazioni nell'istruzione di assegnamento. Una volta che ottenete 0, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2,... ci siete quasi, basta cambiare il valore iniziale di turno...
  2. E se si vuole che turno assuma ciclicamente i valori 0, 2, 4 e 6? Suggerimento: Beh, è molto simile al problema iniziale: invece di incrementare turno turno di 1 lo incrementerete di ...
  3. E se si vuole che turno assuma ciclicamente i valori 1, 3, 5 e 7? Suggerimento: partendo dalla versione precedente, e con una modifica simile a quella fatta in precedenza...
  4. (*) E se si vuole che turno assuma ciclicamente i valori 2, 4, 8 e 16?
  5. (*) E se si vuole che turno assuma ciclicamente i valori 2, 4, 16 e 256? Suggerimento: è più semplice se invertite l'ordine delle istruzioni di assegnamento a turno e di visualizzazione (System.out.println).

Soluzione

  1. Seguiamo il suggerimento e ripartiamo dalla versione originale, ossia
        turno = (turno + 1) % 4;
    
    e invertiamo l'ordine di "+" e "%", ottenendo:
        turno = turno % 4 + 1;
    
    Così facendo, dapprima turno viene "confinato" fra 0 e 3 dall'operatore "%" e poi al valore così ottenuto viene aggiunto 1. Questa istruzione fa proprio quello che ci serve, ma se la inseriamo nel codice otteniamo
    class Turno {
        public static void main(String[] args) {
    	int turno;
    	int i;
    	turno = 0; 
    	i = 0;
    	while (i <= 20) {
    	    System.out.println("Giro numero " + i + " Ora e' di turno giocatore" + turno);
    	    turno = turno % 4 + 1;
    	    i = i + 1;
    	}
        }
    }
    
    I valori visualizzati sono
    >java Turno
    Giro numero 0 Ora e' di turno giocatore 0
    Giro numero 1 Ora e' di turno giocatore 1
    Giro numero 2 Ora e' di turno giocatore 2
    Giro numero 3 Ora e' di turno giocatore 3
    Giro numero 4 Ora e' di turno giocatore 4
    Giro numero 5 Ora e' di turno giocatore 1
    Giro numero 6 Ora e' di turno giocatore 2
    Giro numero 7 Ora e' di turno giocatore 3
    Giro numero 8 Ora e' di turno giocatore 4
    Giro numero 9 Ora e' di turno giocatore 1
    ... (e altri eliminati)
    
    Quindi, in questo modo, otteniamo la sequenza di valori 0, 1, 2, 3, 4, 1, 2, 3, 4, ... Ci manca ancora una piccola modifica, perchè il primo valore (0) non è quello voluto. Come ci dice il suggerimento, basta modificare il valore iniziale di turno, da 0 a 1, ottenendo la soluzione corretta:
    class Turno {
        public static void main(String[] args) {
    	int turno;
    	int i;
    	turno = 1; //Era zero
    	i = 0;
    	while (i <= 20) {
    	    System.out.println("Giro numero " + i + " Ora e' di turno giocatore" + turno);
    	    turno = turno % 4 + 1;
    	    i = i + 1;
    	}
        }
    }
    
  2. Vogliamo quindi un'istruzione che faccia assumere a turno i valori 0, 2, 4, 6, 0, 2, 4, 6, ... Far passare turno da 0 a 2, da 2 a 4 e da 4 a 6 è semplicissimo, basta incrementare di due invece che di uno. Passare da 6 a 0 è ancora semplice: se aggiungiamo 2 otteniamo 8, e a questo punto basta prendere il modulo 8 per "confinare":
        turno = (turno + 2) % 8;
    
    Il programma per verificare la correttezza è (ricordatevi di far partire turno da 0 e non da 1 come nell'ultima attività...):
    class Turno {
        public static void main(String[] args) {
    	int turno;
    	int i;
    	turno = 0; 
    	i = 0;
    	while (i <= 20) {
    	    System.out.println("Giro numero " + i + " Ora e' di turno giocatore" + turno);
                turno = (turno + 2) % 8;
    	    i = i + 1;
    	}
        }
    }
    
  3. Per passare da 0, 1, 2, 3, 0, 1, ... a 1, 2, 3, 4, 1, 2, ... avevamo cambiato il valore iniziale di turno da 0 a 1. Qui la modifica è analoga: per passare da 0, 2, 4, 6, 0, 2, 4, 6, ... a 1, 3, 5, 7, 1, 3, 5, 7, ... basta far partire turno da 1. Il codice è:
    class Turno {
        public static void main(String[] args) {
            int turno;
            int i;
            turno = 1; 
            i = 0;
            while (i <= 20) {
                System.out.println("Giro numero " + i + " Ora e' di turno giocatore " + turno);
                turno = (turno + 2) % 8;
                i = i + 1;
            }
        }
    }
    
  4. La cosa si fa un po' più complicata, ma non spaventatevi. Il valore iniziale della sequenza è 2, e quindi il valore iniziale di turno sarà, analogamente a quanto visto in precedenza, 2. L'istruzione di "incremento" è invece diversa. La sequenza 2, 4, 8, 16 è infatti di natura diversa dalle precedenti: finora ottenevamo il valore successivo aggiungendo al valore precedente una quantità costante. Ora, invece, dovremmo aggiungere una quantità variabile: 2 per passare da 2 a 4, 4 per passare da 4 a 8, 8 per passare da 8 a 16 e 16 per passare da 16 a 32. Si potrebbe trovare una soluzione basandosi su questo schema, ma con un passo di ragionamento ulteriore possiamo trovare una soluzione migliore.
    E il passo ulteriore consiste nel trovare un altro modo per passare da ogni elemento della sequenza al successivo in modo "costante" (ossia, con la stessa operazione). Ma ogni elemento della sequenza è il doppio del precedente! Quindi, con un'istruzione del tipo
        turno = turno * 2;
    
    otteniamo (quasi) quello che vogliamo. Il "quasi" dipende dal fatto che resta ancora da capire come "confinare" i valori. Ragioniamo così: osserviamo che quando turno vale 16, l'istruzione precedente lo porterebbe a 32, invece che a 2. Ma per passare da 32 a 2 senza modificare il risultato degli altri raddoppi basta prendere il modulo 30 (32 % 30 dà proprio 2):
        turno = (turno * 2) % 30;
    
    (le parentesi non sono necessarie, ma mettiamole per chiarezza). Il risultato finale quindi è:
    class Turno {
        public static void main(String[] args) {
            int turno;
            int i;
            turno = 2; 
            i = 0;
            while (i <= 20) {
                System.out.println("Giro numero " + i + " Ora e' di turno giocatore " + turno);
                turno = (turno * 2) % 30;
                i = i + 1;
            }
        }
    }
    
  5. Seguiamo il suggerimento e otteniamo una prima versione del programma (ancora incompleta e non compilabile: i "??" vanno sostituiti da codice "vero"):
    class Turno {
        public static void main(String[] args) {
            int turno;
            int i;
            turno = ??; 
            i = 0;
            while (i <= 20) {
                turno = ??;
                System.out.println("Giro numero " + i + " Ora e' di turno giocatore " + turno);
                i = i + 1;
            }
        }
    }
    
    Osserviamo poi che per passare da ogni elemento della sequenza 2, 4, 16, 256 al successivo basta moltiplicare turno per se stesso, quindi avremo un'istruzione del tipo:
         turno = (turno * turno) % ("qualcosa ancora da capire");
    
    Per completare questa istruzione si procede come in precedenza: quando turno raggiunge il valore massimo (256), all'esecuzione dell'istruzione deve assumere il valore minimo della sequenza (2), e non il quadrato di 256. Questo si ottiene come al solito con il modulo. Facciamo fare i conti al Java e scriviamo:
         turno = (turno * turno) % (256 * 256 - 2);
    
    Manca solo il valore iniziale di turno, che deve essere tale per cui alla prima esecuzione dell'istruzione precedente turno assuma il valore 2. Si potrebbe pensare alla radice quadrata di 2, ma non funzionerebbe perchè stiamo lavorando con numeri interi. L'intuizione corretta si ha osservando che cosa succede alla fine della sequenza (ossia quando si passa da 256 a 2): infatti, l'istruzione precedente funziona alla perfezione in quel caso, con turno che vale dapprima 256 e poi 2. Ma allora sfruttiamo questo fatto e assegnamo a turno il valore iniziale 256, ottenendo:
    class Turno {
        public static void main(String[] args) {
            int turno;
            int i;
            turno = 256; 
            i = 0;
            while (i <= 20) {
                turno = (turno * turno) % (256 * 256 - 2);
                System.out.println("Giro numero " + i + " Ora e' di turno giocatore " + turno);
                i = i + 1;
            }
        }
    }
    

Fate l'amore e non la guerra! Valid HTML 4.01! Valid CSS! Stefano Mizzaro
Last modified: 2003-06-12