Oppure

Loading
06/09/18 9:14
comtel
Buongiorno ragazzi, so che sto inondando il forum di richieste riguardanti il C, ma scrivendo un piccolo codice per il prodotto riga per colonna tra due matrici utilizzando gli interi generati pseudocasualmente come elementi della matrice mi sono imbattuto in questo tipo di errore logico, ossia che la matrice risultante non è quella che ci si aspettava che sia. Il problema poteva stare proprio nell'elaborazione della funzione, ma l'esercizio è una versione successiva ad un altro esercizio uguale utilizzando i float come elementi pseudocasuali delle due matrici, ed in questo caso funziona perfettamente. Non so il perchè, ho anche provato ad utilizzare la calloc piuttosto che la malloc.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int * row_allocation(int *, int *, int, int, int, int);

int main(int argc, char *argv[]) {
	int row1 = atoi(argv[1]), col1 = atoi(argv[2]), row2 = atoi(argv[3]), col2 = atoi(argv[4]);
	int *mat1 = (int *)malloc(row1*col1*sizeof(int)); 
	int *mat2 = (int *)malloc(row2*col2*sizeof(int));
	int i, j; 

	srand((unsigned int)time(NULL));

	printf("\nPrima matrice:\n");
	for(i = 0; i < row1; i++) {
		for(j = 0; j < col1; j++) {
			*(mat1 + i*row1 + j) = ((int)rand())/((int)RAND_MAX/50); 
		      	printf("%d\t", *(mat1 + i*row1 + j));	
		} printf("\n");
	}

	printf("\nSeconda matrice:\n");
	for(i = 0; i < row2; i++) {
		for(j = 0; j < col2; j++) {
			*(mat2 + i*row2 + j) = ((int)rand())/((int)RAND_MAX/50); 
			printf("%d\t", *(mat2 + i*row2 + j));
		} printf("\n");
	}

	printf("\nMatrice prodotta per righe:\n");
	int *mt3 = row_allocation(mat1, mat2, row1, row2, col1, col2);
	for(i = 0; i < row1; i++) {
		for(j = 0; j < col2; j++) {
			printf("%d\t", *(mt3 + i*row1 + j));
		} printf("\n");
	}

	free(mat1); free(mat2); free(mt3); 
}

int * row_allocation(int *mat1, int *mat2, int row1, int row2, int col1, int col2) {
	int *mat3 = (int *)malloc(row1*col2*sizeof(int)), i, j, z; 

	for(i = 0; i < row1; i++) {
		for(j = 0; j < col2; j++) {
			for(z = 0; z < row2; z++) {
				*(mat3 + i*row1 + j) += (*(mat1 + i*row2 + z)) * (*(mat2 + z*col2 + j)); 
			} printf("%d\t", *(mat3 + i*row1 + j));
		} printf("\n");
	} printf("\n\n"); 
	
	return mat3; 
}


Inoltre vorrei sapere cosa si intende per allocazione della matrice per colonne.. Cioè, ho inteso come si visualizza una matrice per colonne, ma per allocarla non bisogna comunque utilizzare la malloc o la calloc specificando la lunghezza della porzione di memoria, ossia M*N*sizeof(tipo) indifferentemente dal tipo di allocazione della matrice ?!
Ultima modifica effettuata da comtel 06/09/18 9:23
aaa
06/09/18 9:34
Mikelius
Posta pure l'altro esercizio.

Cmq, c'è un piccolo errore concettuale.

 int *mat1 = (int *)malloc(row1*col1*sizeof(int));

Alloca lo spazio per un ARRAY di (row1*col1) elementi, NON una matrice.
Per la matrice PRIMA allochi le righe POI, PER OGNI RIGA, allochi lo spazio per le colonne...tipo:
/*  righe (ogni elemento è un puntatore) */
mat1 = (int**)malloc( row1 * sizeof(int) );

/* Colonne */
for(i=0; i<row1; i++)
    mat1[i] = (int*)malloc( col1 * sizeof(int) );
Ultima modifica effettuata da Mikelius 06/09/18 10:12
aaa
06/09/18 9:42
lumo
Ci sono due errori in effetti, uno di logica nella moltiplicazione, e l'altro di inizializzazione della memoria anche se meno evidente.

Controlla bene la definizione di moltiplicazione di matrice, la riga che la effettua dovrebbe essere corretta così:
*(mat3 + i*row1 + j) += (*(mat1 + i*row1 + z)) * (*(mat2 + z*row2 + j));


Per quanto riguarda l'inizializzazione, dovresti assicurarti che il valore contenuto in *(mat3 + i*row1 +j) sia 0 prima di procedere a un += (che somma sul valore già presente, ma il valore già presente è indefinito)
Di solito la memoria è quasi sempre inizializzata a zero per motivi che che ti sono stati accennati nel thread su calloc, ma non c'è questa garanzia in generale.

Prima del for aggiungi una inizializzazione (oppure usa calloc). Ti consiglio per chiarezza maggiore di fare così anzi:
int elemento = 0;
for (...) {
    elemento += ...
}
*(mat3 + i*row1 + j) += elemento;


Lo trovo un po' più chiaro da leggere, inoltre eviti ogni volta un accesso inutile alla memoria: tecnicamente ogni volta che fai *(mat3 + i*row1 + j) fai un indirizzamento alla memoria, che è piu lento di usare una variabile locale siccome di solito il compilatore le salva in un registro. Tutto questo discorso a meno che il compilatore non se ne accorga e ottimizzi, ma ho qualche dubbio su questo.
Ultima modifica effettuata da lumo 06/09/18 9:49
aaa
06/09/18 9:48
comtel
Grazie mille per la pronta risposta,
effettuerò dei test con la soluzione che lumo mi ha proposto, ed inoltre ecco qui il codice dell'altro esercizio, che a quanto ho provato e testato funziona:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

float * matx_prod(float *, float *, int, int, int, int);

int main(int argc, char *argv[]) {
	int row1 = atoi(argv[1]), col1 = atoi(argv[2]), row2 = atoi(argv[3]), col2 = atoi(argv[4]), i, j, z;  
	float *mat1, *mat2, *mat3; 
	clock_t ct1, ct2; 
	double e_time; 

	ct1 = clock();
	mat1 = (float *)malloc(row1*col1*sizeof(float));
	mat2 = (float *)malloc(row2*col2*sizeof(float));
	ct2 = clock();
	e_time = (double)(ct2 - ct1)/(double)CLOCKS_PER_SEC;

	printf("Allocazione avvenuta in %e secondi\n", e_time);

	srand((unsigned int)time(NULL));

	printf("\nPrima matrice:\n");
	for(i = 0; i < row1; i++) {
		for(j = 0; j < col1; j++) {
			*(mat1 + i*row1 + j) = ((float)rand())/((float)RAND_MAX/50);	
			printf("%2.2f\t\t", *(mat1 + i*row1 + j));
		} printf("\n");
	}	

	printf("\nSeconda matrice:\n");
	for(i = 0; i < row2; i++) {
		for(j = 0; j < col2; j++) {
			*(mat2 + i*row2 + j) = ((float)rand())/((float)RAND_MAX/50);	
			printf("%2.2f\t\t", *(mat2 + i*row2 + j));
		} printf("\n");
	} 

	ct1 = clock();
	float *matx3 = matx_prod(mat1, mat2, row1, col1, row2, col2);
	ct2 = clock();
	e_time = (double)(ct2 - ct1)/(double)CLOCKS_PER_SEC;

	printf("\nMatrice prodotta in %e secondi:\n", e_time);
	for(i = 0; i < row1; i++) {
		for(j = 0; j < col2; j++) {
			printf("%2.2f\t\t", *(matx3 + i*row1 + j));
		} printf("\n");
	} printf("\n");
}

float * matx_prod(float *mat1, float *mat2, int row1, int col1, int row2, int col2) {
	float *mat3 = (float *)malloc(row1*col2*sizeof(float));
	int i, j, z;

	for(i = 0; i < row1; i++) {
		for(j = 0; j < col2; j++) {
			for(z = 0; z < row2; z++) {
				*(mat3 + i*row1 + j) += (*(mat1 + i*row2 + z)) * (*(mat2 + z*col2 + j)); 
			}
		} 
	}	

	return mat3;
}
aaa
06/09/18 9:56
comtel
Postato originariamente da lumo:

Ci sono due errori in effetti, uno di logica nella moltiplicazione, e l'altro di inizializzazione della memoria anche se meno evidente.

Controlla bene la definizione di moltiplicazione di matrice, la riga che la effettua dovrebbe essere corretta così:
*(mat3 + i*row1 + j) += (*(mat1 + i*row1 + z)) * (*(mat2 + z*row2 + j));



Osservando la riga di codice, ho fatto un test, quando i = 0; j = 0; z = 1;
Allora la riga dovrebbe essere la seguente: mat3 (ossia primo elemento della matrice risultante) = mat1 + 1 (fin qui ci siamo, ossia il secondo elemento della riga della matrice mat1) * mat2 + 3 (ossia il quarto elemento della matrice mat2, che corrisponde a quello sulla seconda colonna). Il che non dovrebbe andare bene dato che la definizione di prodotto riga per colonna è la seguente:

mat1 =
1 2 3
4 5 6

mat2 =
1 2
3 4
5 6

quindi il primo elemento della matrice risultante dovrebbe essere questo => elemento(1:1) = (1*1 + 2*3 + 3*5).. E così via.

Interessante è però la tua osservazione sulla inizializzazione dell'elemento prima di utilizzarlo, anche se ho utilizzato anche una calloc, che inizializza gli elementi a 0 al suo richiamo. La tua logica di non accedere sempre alla memoria ad ogni passo del ciclo è giusta, ma comunque non ha risolto il mio problema.
Ultima modifica effettuata da comtel 06/09/18 10:04
aaa
06/09/18 10:01
comtel
Postato originariamente da Mikelius:

Posta pure l'altro esercizio.

Cmq, c'è un piccolo errore concettuale.

 int *mat1 = (int *)malloc(row1*col1*sizeof(int));

Alloca lo spazio per un ARRAY di (row1*col1) elementi, NON una matrice.
Per la matrice PRIMA allochi le righe POI, PER OGNI RIGA, allochi lo spazio per le colonne...tipo:

/* righe (ogni elemento è un puntatore) */
mat1 = (int**)malloc( row1 * sizeof(int) );

/* Colonne */
for(i=0; i<row1; i++)
mat1[i] = (int*)malloc( col1 * sizeof(int) );


Correggetemi se sto sbagliando qualcosa. E' vero che sto allocando in questo modo un array, ma in memoria una matrice viene comunque vista come un array di N elementi, per tale ragione si accede agli elementi dell'array in quel modo, ad esempio: *(mat1 + i*RIGA + j).
aaa
06/09/18 10:16
Mikelius
Infatti ho scritto CONCETTUALE.
Ai fini pratici per questo esercizio non da problemi.

Un'altro errore, ma di DIMENTICANZA , è non eseguire i controlli sulle dimensioni delle due matrici (potrei cassare come parametri Stringhe, Numeri negativi...oppure semplicemente righe e colonne di matrici non moltiplicabili.
Queste non sono errori che precludono il funzionamento del programma in se, ma non lo rendono affidabile, tutto qui.

(P.s. hai dichiarato int main(....) è bene far restituire un valore al main. )
Ultima modifica effettuata da Mikelius 06/09/18 10:22
aaa
06/09/18 10:19
comtel
Postato originariamente da Mikelius:

Infatti ho scritto CONCETTUALE.
Ai fini pratici per questo esercizio non da problemi.


Va bene, va bene.
Intanto ancora devo risolvere il problema del prodotto (ho anche provato con la soluzione di lumo, per quanto secondo i miei test non sia del tutto esatta), ma continuerò con l'esercizio sperando di trovarla successivamente.
aaa