Le matrici

Realizzare un programma in linguaggio C che acquisisca da tastiera una matrice di interi 4x4. I valori degli elementi devono essere compresi nell'intervallo [-5, 5]. I valori non validi eventualmente immessi devono essere scartati e non contribuire al conteggio dei valori inseriti.

La seguente sequenza di valori immessi:

0
10
1
2
3
1
3
2
4
2
3
4
5
-8
3
5
4
0

corrisponderà alla matrice:

0 1 2 3
1 3 2 4
2 3 4 5
3 5 4 0

Suggerimenti

Per rendere il programma pronto a gestire matrici di dimensione diversa da quella specificata, conviene definire le seguenti costanti in forma di macro:

#define NRIG (4)
#define NCOL (4)

e formulare tutto il programma utilizzando opportunamente questi valori. Se un domani servisse per esempio gestire delle matrici 6x3 (o qualsiasi altra dimensione) basterà modificare queste definizioni per adattare automaticamente l'intero programma alla nuova richiesta.

Nella funzione main si dichiari una matrice NRIG x NCOL di interi per ospitare i valori letti (vedi S7.1.1 per la sintassi della dichiarazione).

La lettura dei dati può essere svolta con due cicli for annidati, i cui contatori vengono usati come indici di riga e colonna (si vedano per esempio i cicli in S9.3). Per esempio

for (i = 0; i < NRIG; i++) {
  for (j = 0; j < NCOL; j++) {
    do {
      /* legge un numero e e lo memorizza */
      mat[i][j] = ....
    } while(mat[i][j] > MAX || mat[i][j] < MIN);
  }
}

All'interno del ciclo annidato si dovrà ripetere la lettura del valore - con un altro ciclo - fintanto che esso non è incluso nell'intervallo richiesto [-5, 5], opportunamente indicati con MIN e MAX. Questo terzo ciclo può essere implementato con il costrutto do-while (S4.8), in modo da eseguirlo almeno una volta, il quale contiene le istruzioni di lettura fgets e atoi (S4.8).

1) Stampa della matrice

Stampare la matrice letta col seguente formato:

[MATRICE]
0 1 2 3
1 3 2 4
2 3 4 5
3 5 4 0

Suggerimenti

Supponendo che i valori memorizzati nella matrice siano di tipo int, si può definire una funzione dichiarata come segue:

void stampa_matrice(int mat[NRIG][NCOL]);

La stampa dei dati può essere svolta con due cicli for annidati, i cui contatori vengono usati come indici di riga e colonna, in modo simile all'esempio sopra riportato (si veda anche l'esempio in S9.3).

Si può utilizzare l'istruzione putchar('\n') per stampare l'andata a capo dopo aver stampato i 4 valori su una riga. La funzione putchar è una versione specializzata della putc presentata in S8.3; la funzione precedente equivale a putc('\n', stdout).

2) Media per riga

Calcolare la media di ogni riga della matrice.

Stampare il risultato con il formato seguente (nel caso della matrice di esempio):

[MEDIA_RIGHE]
1.500
2.500
3.500
3.000

Suggerimenti

Un modo intelligente di risolvere questo problema è di definire una funzione dichiarata come segue:

double media_riga(int mat[NRIG][NCOL], int riga);

che calcola la media dei valori della matrice mat sulla riga riga.

La funzione media_riga conterrà un ciclo for che, tenendo fisso l'indice per la riga pari a riga, somma i 4 elementi della riga e divide la somma per 4, restituendo il risultato. Non dimenticare di inizializzare a 0 la variabile che serve a memorizzare la somma dei valori, altrimenti il calcolo si baserà su un valore iniziale aleatorio.

La funzione media_riga verrà richiamata da un ciclo for nella funzione main per la stampa della media di ciascuna delle quattro righe.

3) Media per colonna

Calcolare la media di ogni colonna della matrice.

Stampare il risultato con il formato seguente (nel caso della matrice di esempio):

[MEDIA_COLONNE]
1.500
3.000
3.000
3.000

Suggerimenti

Un modo intelligente di risolvere questo problema è di definire una funzione dichiarata come segue:

double media_colonna(int mat[NRIG][NCOL], int col);

che calcola la media dei valori della matrice mat sulla colonna col.

La funzione media_colonna conterrà un ciclo for che, tenendo fisso l'indice per la colonna pari a col, somma i 4 elementi della colonna e divide la somma per 4, restituendo il risultato. Non dimenticare di inizializzare a 0 la variabile che serve a memorizzare la somma dei valori, altrimenti il calcolo si baserà su un valore iniziale aleatorio.

La funzione media_colonna verrà richiamata da un ciclo for nella funzione main per la stampa della media di ciascuna delle quattro colonne.

La logica interna della funzione media_colonna è simile a quella di media_riga, solo che il ciclo all'interno di media_colonna tiene fissa la colonna e ne somma gli elementi, mentre media_riga tiene fissa la riga.

4) Media di tutti gli elementi

Calcolare la media di tutti gli elementi della matrice.

Stampare il risultato con il formato seguente (nel caso della matrice di esempio):

[MEDIA]
2.625

Suggerimenti

Si definisca una funzione dichiarata come segue:

double media(int mat[NRIG][NCOL]);

Nella funzione media potranno essere utilizzati due cicli for annidati per indicizzare tutti gli elementi e sommarne il valore ad una opportuna variable somma. Il valore di somma andrà diviso per 16 per il calcolo della media.

Non dimenticare di inizializzare la variabile somma a zero, altrimenti il calcolo della media si baserà su un valore iniziale aleatorio.

5) Frequenza dei valori

Stampi a video le frequenze di occorrenza dei valori usando il formato:

[FREQUENZE]
0
0
0
0
0
2
2
3
4
3
2

Nella prima riga si riporti le frequenze dei -5, nella seconda le frequenze dei -4, ecc.

Suggerimenti

La soluzione può essere realizzata con una funzione come la seguente:

void frequenze(int mat[NRIG][NCOL], int freq[]);

La funzione frequenze riceve due parametri: la matrice mat contenente i numeri di cui calcolare la frequenza, e il vettore delle frequenze. Un esempio di calcolo delle frequenze è riportato in (S6.8).

Tutti i parametri devono essere dichiarati nel main che chiama la funzione frequenze.

Fai attenzione all'inizializzazione dei valori del vettore che conterrà le frequenze, in quanto:

  • l'inizializzazione va fatta nel main;
  • come tutte le variabili, un vettore non inizializzato contiene elementi i cui valori sono aleatori;
  • l'inizializzazione dei vettori è illustrata in S5.9.2.

La stampa delle frequenze avvenga con un ciclo for nel main, dopo aver stampato il marcatore.

La logica per il calcolo delle frequenze è la stessa suggerita nel tutorato relativo ai vettori. La differenza è che ora si dovranno svolgere due cicli per esaminare tutte le righe e le colonne, invece che un solo ciclo che esamina gli elementi del vettore.

6) Rotazione della matrice

Si ruoti la matrice di 90 gradi in senso orario.

Data la matrice d'esempio:

0 1 2 3
1 3 2 4
2 3 4 5
3 5 4 0

la matrice ruotata deve essere:

3 2 1 0
5 3 3 1
4 4 2 2
0 5 4 3

e il programma deve stampare:

[RUOTA]
3 2 1 0 
5 3 3 1 
4 4 2 2 
0 5 4 3

Suggerimenti

Si crei una funzione come la seguente:

void ruota(int output[NRIG][NCOL], const int input[NRIG][NCOL]);

La funzione conterrà gli usuali due cicli for annidati che, per ciascun valore della matrice input lo assegnano all'elemento corretto della matrice output. Si ricordi che i due argomenti sono degli array, pertanto il passaggio dei parametri avviene per riferimento. Pertanto, modificando nella funzione ruota il valore di un elemento di output, ne risulterà modificato il valore nella variabile passata come argomento. Non c'è rischio di modificare i valori della matrice input in quanto l'argomento è dichiarato const.

NOTA: per la rotazione, il segreto è gestire correttamente gli indici delle matrici!

Se non riesci a trovare la soluzione.... scopri un piccolo aiuto »

Verifica automatica

Si utilizzi il tool pvcheck di verifica automatica per testare il corretto funzionamento del programma (maggiori informazioni circa l'uso di pvcheck sono disponibili qui).

Il file contenente i test è matrici.test.

Il comando da eseguire per il test è il seguente:

pvcheck -f matrici.test ./a.out

Nella prossima pagina potrai esaminare un esempio di soluzione dell'esercizio.