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
è...
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
...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 ...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...turno
assuma ciclicamente i valori
2, 4, 8 e 16?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
).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; } } }
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; } } }
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; } } }
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.
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; } } }
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; } } }