Aritmetica modulare e gestione circolare degli interi

Aritmetica modulare e gestione
circolare degli interi
●
●
Scrivere un programma che abbia come
parametri della riga di comando:
–
un numero intero k
–
una sequenza di stringhe
e restituisca la sequenza di stringhe crittografate
secondo il metodo di Giulio Cesare (ogni
carattere viene sostituito con il k-esimo carattere
successivo nell'ordinamento alfabetico; l'alfabeto
è utilizzato circolarmente ovvero dopo la 'z' si
ricomincia dalla 'a')
Aritmetica modulare
●
●
Ad es. gestire circolarmente l'indice di un array
consente di rientrare nell'array dalla parte
opposta (se si supera n-1 si passa alla
posizione 0 e viceversa).
Nella crittografia richiesta, se si oltrepassa la 'z'
si rientra dalla lettera 'a' e viceversa
'la' =>'vk'
'vispa' =>'fsczk'
'teresa' =>'dobock'
'avea' =>'kfok'
Soluzione 1
void codif( unsigned char *s,
unsigned char k){
}
while (*s){
*s =*s + k;
if (*s > 'z')
*s = *s - 26;
s++;
Una prima soluzione consiste nel verificare se il
}
valore della variabile ha superato il massimo; in
tal caso si sottrae 26 al valore ottenuto. Il fatto di
utilizzare comunque delle rappresentazioni
limitate nel numero di bit pone dei problemi ...
char e unsigned char
void codif( unsigned char *s,
unsigned char k){
}
while (*s){
*s =*s + k;
if (*s > 'z')
*s = *s - 26;
s++;
}
char in C è un intero con
segno a 8 bit (-128 .. +127)
unsigned char è un intero
senza segno a 8 bit (0..255)
Se il nuovo carattere è oltre
la 'z' ricomincia dalla 'a'.
char e unsigned char
●
●
char è un intero con segno a 8 bit (-128 .. +127)
se *s + k supera 127 si ha l'overflow e il risultato
è considerato negativo
–
●
es: 'z'+10 = 122 + 10 = -124 ('z' è codificata
in ASCII con 122)
Il test successivo (*s > 'z') non viene
soddisfatto e il carattere risultante non sarà
sicuramente quello che ci si aspetta
–
nell'esempio invece di 'j' (il decimo carattere
circolarmente dopo la 'z') si otterrà un carattere
diverso (il 132 del codice ASCII)
char e unsigned char
●
●
unsigned char è un intero senza segno a 8 bit
(0.. 255)
in tal caso il risultato della somma viene
considerato sempre positivo e il test viene
soddisfatto.
–
'z' + 10 = 122 +10 = 132
–
132 > 'z'risulta vero e quindi il valore assegnato
a *s sarà 132-26=106 che corrisponde al
carattere 'j'
Aritmetica modulare
●
In generale gestire circolarmente gli interi
significa realizzare una aritmetica modulare in
cui ad ogni intero k corrisponde un intero
compreso tra 0 e n-1
0
1
2
..
n-1
n
n+1
...
2n-1
2n
..
0
1
2
..
n-1
0
1
...
n-1
0
..
Aritmetica modulare
●
Il problema si pone per i numeri negativi. Nella
gestione circolare di un indice ad esempio,
dovremo avere questa corrispondenza:
0
-1
-2
..
-(n-1)
-n
-(n+1)
...
-(2n-1)
-(2n)
..
0
n-1
n-2
..
1
0
n-1
..
1
0
..
In tal modo il resto ha sempre un valore
positivo:
n=q⋅d r
n : dividendo
q : quoziente (l'intero più grande che
moltiplicato per d fornisce un risultato
minore o uguale a n)
r : resto della divisione (sempre positivo)
Es. resto della divisione per 5
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1
2
3
4
...
5 6
7
8
9 10 11 12 13 14
...
0 1
2
3
4
Operatore %
●
L'operatore % di resto della divisione se
applicato a dividendi negativi fornisce il resto
negativo. Es.
-7 % 5 = -2
●
Per ottenere il risultato desiderato si può
sommare al resto così ottenuto (compreso tra (d-1) e 0 ) il divisore (in modo da avere un valore
tra 1 e d) ed effettuare di nuovo il resto.
Resto della divisione per 5
n
-11
-10
-9
-8
-7
-6
-5
-4
-3
-2
-1
0
1
n%5
-1
0
-4
-3
-2
-1
0
-4
-3
-2
-1
0
1
n%5+5
4
5
1
2
3
4
5
1
2
3
4
5
6
(n%5+5)%5
4
0
1
2
3
4
0
1
2
3
4
0
1
Resto della divisione per 5
n
-1
0
1
2
3
4
5
6
7
8
9
10
11
n%5
-1
0
1
2
3
4
0
1
2
3
4
0
1
n%5+5
4
5
6
7
8
9
5
6
7
8
9
5
6
(n%5+5)%5
4
0
1
2
3
4
0
1
2
3
4
0
1
Aritmetica modulare
●
Utilizzando l'operatore di modulo (resto della
divisione) si può ottenere una soluzione più
significativa che considera anche gli
spostamenti k negativi
Soluzione alternativa
main(int argc, char *argv[]){
int i;
char k;
Nel main il valore di k può
anche essere negativo
k=atoi(argv[1]);
}
for (i=2; i < argc; i++){
printf("'%s' =>",argv[i]);
codif(argv[i],k);
printf("'%s'\n",argv[i]);
}
Soluzione alternativa
void codif(char *s, char k){
while (*s){
if ('a' <= *s && *s <= 'z'){
char x=*s-'a';
Dopo aver calcolato la
distanza da 'a' si somma k e
x= (x+k) % 26;
prendendo il resto modulo 26
si rientra in un valore 0..25
if (x<0)
x+=26;
A meno che k sia negativo
*s = 'a'+x;
per cui x potrebbe risultare
}
negativo anch'esso (ma
inferiore in modulo a 26);
s++;
basta sommare 26 e si
}
torna a posto
}
Soluzione alternativa 1
void codif(char *s, char k){
while (*s){
if ('a' <= *s && *s <= 'z'){
char x=*s -'a'+k;
Dopo aver calcolato la
distanza da 'a' si somma k
x=(x%26+26)%26;
ottenendo un numero che
può anche essere negativo
*s = 'a'+x;
}
s++;
x%26 sarà un numero compreso tra -25 e
+25; sommando 26 si ottiene un numero
}
compreso tra 1 e 51 (quindi solo positivi); un
ulteriore modulo restituisce il valore tra 0 e
}
25. Ad es. se x=-1 (un carattere prima di 'a' e
quindi 'z') si ottiene (-1 % 26 +26)%26 = (-1
+26)%26 = 25%26=25