giovedì 25 settembre 2008

Le rotazioni in 3 dimensioni

Le rotazioni dello spazio a 3 dimensioni in matematica e in fisica sono trattabili in termini di particolari matrici, dette matrici ortogonali la cui matrice inversa coincide con la trasposta (per matrice trasposta di R si intende la matrice RT che si ottiene scambiando righe con colonne di R). In formula, detta R una tale matrice RRT=1. Una matrice ortogonale tridimensionale può sempre, grazie ad una opportuna trasformazione di similitudine, essere posta nella forma:
Rotations
Inoltre le righe e le colonne di una matrice di rotazione formano una base ortonormale. Tali matrici sono per definizione invertibili e formano quindi un sottogruppo di GL(3), denotato con O(3). Immaginiamo ora di considerare una rotazione infinitesima cioè una matrice vicina alla matrice identica R=1+E, dove le entrate della matrice E sono infinitesime. Allora:
Rotations
dove a,…,k sono infinitesimi. Allora prodotti e potenze di tali coefficienti possono essere trascurati nei successivi conti in quanto infinitesimi di ordine superiore al primo.
Consideriamo la matrice infinitesima scritta sopra e imponiamo che rappresenti una rotazione:
Rotations
Tale prodotto, a meno di infinitesimi di ordine superiore al primo vale:
Rotations
Da questa ultima relazione segue che i parametri g, h, k devono essere posti a zero, mentre:
Rotations
Queste condizioni mi permettono di scrivere una generica rotazione infinitesima nella forma:
Rotations
dove sono per comodità state introdotte le matrici Lk dette generatori infinitesimi del gruppo delle rotazioni nel senso che ogni matrice infinitesima può scriversi come loro combinazione lineare. Inoltre si sono ridefiniti i coefficienti opportunamente: a=γ, b=-β, c=α. Si trova facilmente che:
Rotations
Essendo questa espansione vera a meno di infinitesimi di ordine superiore al primo si può pensare che in forma finita una rotazione con parametri α, β e γ sia data da:
Rotations
in quanto exp A~1+A+(termini trascurati). A questo punto si noti che il vettore (α β γ) è invariante sotto la rotazione infinitesima R in quanto R(α β γ)=(α β γ). Quindi sicuramente tale vettore stabilisce la direzione dell'asse di rotazione.
Ora considero il generico vettore perpendicolare a v=(α β γ). Esso si scrive come segue:
Rotations
Il modulo di questo vettore vale:
Rotations
Allora sarebbe meglio considerare il generico versore perpendicolare, ottenibile da w dividendo semplicemente per il suo modulo:
Rotations

Applichiamo la rotazione a un tale vettore. Secondo il senso comune, essendo perpendicolare all'asse di rotazione, esso dovrebbe essere variato di una quantità perpendicolare sia all'asse stesso sia al versore r (in direzione tangenziale alla circonferenza di raggio r e con centro sull'asse). Cioè si dovrebbe avere:
Rotations
dove:
Rotations
Vediamo se si trova questo con il calcolo. Intanto poniamo:
Rotations
Allora:
Rotations
Ma:
Rotations
da cui segue che:
Rotations
Tale vettore risulta effettivamente perpendicolare all'asse di rotazione d (cioè al vettore v) e allo stesso r, come un semplicissimo calcolo algebrico mostra immediatamente.
La figura mostra ed è ben noto che, essendo una quantità infinitesima, il modulo ||e|| vale quanto l'arco di circonferenza compreso tra i vettori r ed R(r).Ma la circonferenza ha raggio unitario quindi tale lunghezza d'arco uguaglia l'angolo di rotazione θ. Calcolare tale modulo è un esercizio algebrico coinvolgente un raccoglimento totale del radicando di c. Si ottiene agevolmente:
Rotations

lunedì 15 settembre 2008

E' nata Sofia


La mia piccola è nata il 12 settembre, un venerdi, alle 2:47 di mattina. Che gioia! E' piccola (2810 g) e adorabile. Ha un bel musetto e mangia come una maialina, sempre attaccata al seno di Cry. Bella davvero! Sono proprio contentissimo…
Spesso frigna perchè ha fame ma non mi da nessun fastidio. Non pensavo, avrei detto che sarei stato più insofferente. Meglio così. Vi piace la piccola?

mercoledì 10 settembre 2008

Il tipo "char" nel linguaggio C

Che cosa sono le variabili di tipo char per il linguaggio di programmazione C e come si possono trattare elementarmente? E' interessante notare che ogni char altro non è che un numero intero, o almeno può essere rappresentato come intero decimale, ottale o esadecimale, numero che esprime la codifica ASCII dei caratteri della tastiera.
Per capire questo fatto notiamo che vi è una sostanziale differenza in C tra la scrittura 'A' e la scrittura "A". Infatti le singole virgolette si usano per indicare il carattere (nel senso di char) A che ha una sua codifica ASCII come numero intero. Al contrario "A" è una stringa, un array o un puntatore costituito da due elementi. Il carattere di stringa A e il carattere di terminazione di stringa \0 (che viene interpretato come zero).
Quindi 'A' ha un valore, come numero intero, decimale, ottale o esadecimale. La rappresentazione non ha importanza perchè i calcoli non dipendono dalla base in cui vengono eseguiti. Quindi con i caratteri si possono fare dei calcoli, come somme e sottrazioni. Se ad esempio si volesse sapere se un carattere digitato è una cifra si potrebbe usare il frammento di codice:

char c;
c = getchar(); /* il carattere viene acquisito dalla tastiera */
if( c>='0' && c<='9' ){
return 1;
} else {
return 0;
}

Questo ci fa notare come il carattere tra virgolette singole viene trattato come interi. Ovviamente questo codice funziona solo nel caso in cui, come nell'ASCII, le cifre sono codificate come numeri consecutivi. Anche le lettere minuscole e le maiuscole sono codificate con numeri interi consecutivi. Per completezza scriviamo un codice che permetta di stampare a video i caratteri ASCII (dal 0 al 127, ma con un banale cambio delle costanti FROM e TO si può cambiare il range).
#include<stdio.h>
#define FROM 0
#define TO 127

int main(){
/* Ciclo che mostra la codifica ascii di tutti i caratteri */
int k;
char c[128];
printf("Dec\t char\n");
for(k=FROM;k<TO;k++){
c[k]=k;
printf(" %d\t %c\n", c[k], c[k]);
}
return 0;
}

L'output (piuttosto lungo) è il seguente:
Dec      Char
0
1 ☺
2 ☻
3 ♥
4 ♦
5 ♣
6 ♠
7 /* Questo è il cicalino del computer */
8 /* Questo è lo spazio */
9 /* Questo è il carattere di tabulazione \t */
10 /* Qui c'è il carattere di newline \n */

11 ♂
12 ♀
13
14 ♫
15 ☼
16 ►
17 ◄
18 ↕
19 ‼
20 ¶
21 §
22 ▬
23 ↨
24 ↑
25 ↓
26 →
27 ←
28 ∟
29 ↔
30 ▲
31 ▼
32
33 !
34 "
35 #
36 $
37 %
38 &
39 '
40 (
41 )
42 *
43 +
44 ,
45 -
46 .
47 /
48 0
49 1
50 2
51 3
52 4
53 5
54 6
55 7
56 8
57 9
58 :
59 ;
60 <
61 =
62 >
63 ?
64 @
65 A
66 B
67 C
68 D
69 E
70 F
71 G
72 H
73 I
74 J
75 K
76 L
77 M
78 N
79 O
80 P
81 Q
82 R
83 S
84 T
85 U
86 V
87 W
88 X
89 Y
90 Z
91 [
92 \
93 ]
94 ^
95 _
96 `
97 a
98 b
99 c
100 d
101 e
102 f
103 g
104 h
105 i
106 j
107 k
108 l
109 m
110 n
111 o
112 p
113 q
114 r
115 s
116 t
117 u
118 v
119 w
120 x
121 y
122 z
123 {
124 |
125 }
126 ~
127 ⌂

Il seguente frammento di codice:
printf("0 decimale -> %d, 0 esadecimale -> %x, 0 carattere -> %c\n", '0', '0', '0');

mostra come, usando il carattere '0' e i comandi di formattazione %d, %x e %c si può scrivere il carattere '0' come intero decimale, esadecimale oppure come vero carattere. Infatti l'output è il seguente:
0 decimale -> 48, 0 esadecimale -> 30, 0 carattere -> 0

Notiamo come si possono fare operazioni come sottrazioni e somme tra i caratteri. Infatti il codice:
printf("'8'-'5' decimale -> %d, '8'-'5' esadecimale -> %x\n", '8'-'5', '8'-'5');

il cui output è:
'8'-'5' decimale -> 3, '8'-'5' esadecimale -> 3

che mostra l'indipendenza dell'operazione dalla rappresentazione scelta. Queste caratteristiche dei caratteri e della loro codifica permettono di scrivere agevolmente una funzione che converta un carattere maiuscolo (cioè con codifica compresa tra gli interi 65 e 90) nel corrispondente minuscolo (cioè con codifica compresa tra gli interi 97 e 122), usando il fatto che le minuscole sono separate dalle maiuscole della quantità 'a'-'A' e che quindi una minuscola si ottiene dalla maiuscola corrispondente aggiungendo tale quantità:
char tolowercase(char c);

char tolowercase(char c){
/* Se c è un carattere maiuscolo */
if( c>='A' && c<='Z' ){
return c + ('a' - 'A');
} else {
return c;
}
}

In modo analogo si ppotrebbe definire la funzione touppercase per convertire un carattere minuscolo in uno maiuscolo sottraendo laddove abbiamo aggiunto. Per oggi è tutto. A presto con altri approfondimenti sul fantastico mondo del linguaggio di programmazione C.

sabato 6 settembre 2008

Linguaggio C: uno sguardo ai puntatori e agli array.

Il linguaggio di programmazione in assoluto più versatile e quindi usato, almeno in ambiente UNIX/LINUX è sicuramente il C. Anche se non così fortemente tipizzato come altri linguaggi (ad esempio Java e Pascal), il C è avido di dichiarazioni e definizioni precise e inoltre non possiede il tipo stringa, che quindi deve essere implementato con gli Array di caratteri o, equivalentemente, grazie ai puntatori.
In generale se si ha a che fare con input e stringhe le inclusioni da fare sono:

#include<stdio.h>  // per la gestione dell'input (scanf), dell'output (printf) e
// dei files (macro FILE)

#include<stdlib.h> // per le funzioni di gestione dinamica della memoria
#include<ctype.h> // per le funzioni per la classificazione dei caratteri
#include<string.h> // per la manipolazione delle stringhe

Veniamo al modo in cui si definisce una stringa in C.
Una possibilità è quella di usare i puntatori a carattere. Definiamo un puntatore a carattere al modo solito:
char* stringa;

A questo punto necessitiamo di allocare della memoria in modo che il compilatore sappia quante celle lasciare libere per la nostra stringa. Per fare questo si usa la funzione della libreria standard (in stdlib.h si trova il suo prototipo) malloc che si occupa di allocare una certa quantità di memoria dinamicamente. A programma terminato dovremo liberare la memoria allocata con la funzione free.
stringa = malloc(64);

free(stringa)

Un'altra possibilità si ha usando gli array di caratteri. Gli array di caratteri sono delle liste ordinate di caratteri, cioè di dati tipo char. Di tale lista deve essere dichiarata in anticipo la lunghezza, in questo modo:
char array[64];  //  lista di 64 caratteri

Ciò significa che in fase di compilazione il compilatore allocherà 64 spazi liberi contigui di memoria per contenere i caratteri del nostro array.
A questo punto la cosa migliore da fare è analizzare del codice di prova:
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>

// Variabili
char array[64];
char* stringa;
int j, k, count;

int main(){
printf("Scrivi una stringa con i puntatori: ");
stringa = malloc(64);
scanf("%s", stringa);
count = strlen(stringa);
printf("Indirizzi di memoria usati:\n");
for( j = 0 ; j < count ; j++ ){
printf("%d° indirizzo - %X \n", (j+1), (stringa+j));
}
printf("Contenuto stringa: %s\n", stringa);
printf("Indirizzo puntato memorizzato in: %X\n", &stringa);
for( k = 0 ; k < count ; k++ ) {
printf("%d° carattere della stringa: *(stringa+%d) -> %c\n", k+1, k, *(stringa+k));
}
free(stringa);
printf("\nScrivi una stringa con gli array: ");
scanf("%s", array);
count = strlen(array);
printf("Indirizzi di memoria usati:\n");
for( j = 0 ; j < count ; j++ ){
printf("%d° indirizzo - %X \n", (j+1), array+j);
}
printf("Contenuto stringa: %s\n", array);
printf("Primo indirizzo puntato con il nome dell'array: %X\n", array);
for( k = 0 ; k < count ; k++ ) {
printf("%d° carattere della stringa: array[%d] -> %c\n", k+1, k, array[k]);
}
return 0;
}

L'output del programma è il seguente:
Scrivi una stringa con i puntatori: prova1
Indirizzi di memoria usati:
1° indirizzo - 804A008
2° indirizzo - 804A009
3° indirizzo - 804A00A
4° indirizzo - 804A00B
5° indirizzo - 804A00C
6° indirizzo - 804A00D
Contenuto stringa: prova1
Indirizzo puntato memorizzato in: 8049AA4
1° carattere della stringa: *(stringa+0) -> p
2° carattere della stringa: *(stringa+1) -> r
3° carattere della stringa: *(stringa+2) -> o
4° carattere della stringa: *(stringa+3) -> v
5° carattere della stringa: *(stringa+4) -> a
6° carattere della stringa: *(stringa+5) -> 1

Scrivi una stringa con gli array: prova2
Indirizzi di memoria usati:
1° indirizzo - 8049A60
2° indirizzo - 8049A61
3° indirizzo - 8049A62
4° indirizzo - 8049A63
5° indirizzo - 8049A64
6° indirizzo - 8049A65
Contenuto stringa: prova2
Primo indirizzo puntato con il nome dell'array: 8049A60
1° carattere della stringa: array[0] -> p
2° carattere della stringa: array[1] -> r
3° carattere della stringa: array[2] -> o
4° carattere della stringa: array[3] -> v
5° carattere della stringa: array[4] -> a
6° carattere della stringa: array[5] -> 2

Da questo semplice programma impariamo come una stringa può essere definita con puntatori e con array. La variabile stringa, cioè il nome del puntatore, punta all'indirizzo del primo carattere della stringa, mentre usando stringa+1, si punta al secondo carattere e così via. Discorso analogo vale per array, in modo che scrivere array+3 equivale a puntare al quarto elemento dell'array.
Se invece si vuole scrivere il contenuto di un singolo carattere, nel caso dei puntatori, si deve usare l'operatore di indirezione *. Allora *stringa contiene il valore del primo carattere puntato, *(stringa+1) il valore del secondo e così via.
Nel caso degli array basta scrivere array[0] per il primo carattere della stringa, array[1] per il secondo e così via.
Le funzioni che accettano stringhe come argomenti dovranno ricevere solo il nome del puntatore o il nome dell'array e automaticamente capiranno che devono considerare il carattere puntato da tale nome e le allocazioni di memoria ad esso contigue fino al carattere di terminazione di stringa \0.
Per quanto riguarda printf e la scanf, si noti come in corrispondenza del nome del puntatore (stesso discorso vale per gli array) si ottiene la stringa intera usando l'istruzione di formattazione %s, mentre si ottiene l'indirizzo di memoria puntato usando l'istruzione di formattazione %X. Analogamente usando l'istruzione di formattazione %c, in corrispondenza di *(stringa+2) nel caso dei puntatori e in corrispondenza di array[2] nel caso degli array, prinft stampa a video il terzo carattere della stringa.
Notare che se non si allocasse memoria con malloc il programma verrebbe compilato comunque se privo di altri errori ma in fase di esecuzione andrebbe in segmentation fault, tipico errore che si ha quando il sistema non trova spazio in memoria allocato per la variabile che sta chiamando oppure quando va a scrivere fuori dal segmento di memoria assegnato, magari pure sovrascrivendo altre variabili (grazie Packz).
Spero che questo post sia abbastanza chiaro anche se mi rendo conto che non esaurisce le problematiche intorno alle stringhe e al loro utilizzo nel linguaggio C. Voi che ne dite? A presto!