Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 15
  1. #1

    [C++] Matrici

    Ciao a tutti. Sto cercando di creare una classe che rappresenti delle matrici (nel senso matematico del termine). Ho utilizzato i template e la classe std::array come contenitore.

    Di seguito il codice che ho scritto. Sapreste dirmi com'é? Funzionale? Buono? Oppure fa schifo ed é meglio lasciar perdere. Mi piacerebbe creare una classe più o meno completa (operazioni di base) che io possa sempre riutilizzare nei miei programmi di fisica computazionale. L'operazione é però più facile a dirsi che a farsi...

    codice:
    #ifndef MATRIX_H
    #define MATRIX_H
    
    #include <array>
    #include <initializer_list>
    #include <cassert>
    
    template <size_t row, size_t col>
    class Matrix
    {
    public:
    	Matrix();
    	Matrix(const std::initializer_list<double>&);
    	Matrix(const Matrix&);
    	virtual ~Matrix();
    	
    	Matrix& operator=(const Matrix&);
    	bool operator==(const Matrix&) const;
        bool operator!=(const Matrix&) const;
        Matrix& operator+=(const Matrix&);
        Matrix& operator-=(const Matrix&);
        Matrix operator+(const Matrix&) const;
        Matrix operator-(const Matrix&) const;
    	double& operator()(int, int);
    	const double& operator()(int, int) const;
    	
    	int get_row() const;
    	int get_col() const;
    	void set(double);
    	
    	
    private:
    	std::array< std::array<double,col>, row> matrix;
    	size_t r;
    	size_t c;
    	
    	void copy(const Matrix&);
    };
    
    #endif
    
    template <size_t row, size_t col>
    Matrix<row,col>::Matrix()
    : r(row), c(col)
    {
    	set(0.);
    }
    
    template <size_t row, size_t col>
    Matrix<row,col>::Matrix(const std::initializer_list<double>& l)
    : matrix(l), r(row), c(col)
    {}
    
    template <size_t row, size_t col>
    Matrix<row,col>::Matrix(const Matrix& m)
    {
    	copy(m);
    }
    
    template <size_t row, size_t col>
    Matrix<row,col>::~Matrix()
    {}
    
    Matrix& Matrix<row,col>::operator=(const Matrix& m)
    {
    	if(this != &m)
    	{
    		copy(m);
    	}
    	
    	return *this;
    }
    
    bool Matrix<row,col>::operator==(const Matrix& m) const
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			if(matrix[i][j] != m.matrix[i][j])
    			{
    				return false;
    			}
    		}
    	}
    	
    	return true;
    }
    
    bool Matrix<row,col>::operator!=(const Matrix& m) const
    {
    	return !( (*this) == m);
    }
    
    Matrix& Matrix<row,col>::operator+=(const Matrix& m)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] += m.matrix[i][j];
    		}
    	}
    	
    	return *this;
    }
    
    Matrix& Matrix<row,col>::operator-=(const Matrix&)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] -= m.matrix[i][j];
    		}
    	}
    	
    	return *this;
    }
    
    Matrix operator+(const Matrix& m) const
    {
    	Matrix tmp<r,c>(*this);
    	
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			tmp.matrix[i][j] += m.matrix[i][j];
    		}
    	}
    	
    	return std::move(tmp);
    }
    
    Matrix operator-(const Matrix& m) const
    {
    	Matrix tmp<row,col>(*this);
    	
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			tmp.matrix[i][j] -= m.matrix[i][j];
    		}
    	}
    	
    	return std::move(tmp);
    }
    
    template <size_t row, size_t col>
    double& Matrix<row,col>::operator()(int i, int j)
    {
    	assert(i > 0 && i <= r);
      	assert(j > 0 && j <= c);
    
    	return matrix[i-1][j-1];
    }
    
    template <size_t row, size_t col>
    const double& Matrix<row,col>::operator()(int i, int j) const
    {
    	assert(i > 0 && i <= r);
      	assert(j > 0 && j <= c);
    
    	return matrix[i-1][j-1];
    }
    
    template <size_t row, size_t col>
    int Matrix<row,col>::get_row() const
    {
    	return r;
    }
    
    template <size_t row, size_t col>
    int Matrix<row,col>::get_col() const
    {
    	return c;
    }
    
    template <size_t row, size_t col>
    void Matrix<row,col>::set(double val)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] = val;
    		}
    	}
    }
    
    template <size_t row, size_t col>
    void Matrix<row,col>::copy(const Matrix& m)
    {
    	// r = m.r; c = m.c ???
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] = m.matrix[i][j];
    		}
    	}
    }
    Grazie a tutti per l'aiuto.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  2. #2
    Scusate. Nel codice precedente c'erano parecchi errori (avevo compilato un'altro programma e quindi ho creduto che fosse tutto ok). Questo é il codice corretto:
    codice:
    #ifndef MATRIX_H
    #define MATRIX_H
    
    #include <array>
    #include <initializer_list>
    #include <cassert>
    
    template <size_t row, size_t col>
    class Matrix
    {
    public:
    	Matrix();
    	Matrix(const std::initializer_list<double>&);
    	Matrix(const Matrix&);
    	virtual ~Matrix();
    	
    	Matrix& operator=(const Matrix&);
    	bool operator==(const Matrix&) const;
        bool operator!=(const Matrix&) const;
        Matrix& operator+=(const Matrix&);
        Matrix& operator-=(const Matrix&);
        Matrix operator+(const Matrix&) const;
        Matrix operator-(const Matrix&) const;
    	double& operator()(int, int);
    	const double& operator()(int, int) const;
    	
    	int get_row() const;
    	int get_col() const;
    	void set(double);
    	
    	
    private:
    	std::array< std::array<double,col>, row> matrix;
    	size_t r;
    	size_t c;
    	
    	void copy(const Matrix&);
    };
    
    #endif
    
    template <size_t row, size_t col>
    Matrix<row,col>::Matrix()
    : r(row), c(col)
    {
    	set(0.);
    }
    
    template <size_t row, size_t col>
    Matrix<row,col>::Matrix(const std::initializer_list<double>& l)
    : matrix(l), r(row), c(col)
    {}
    
    template <size_t row, size_t col>
    Matrix<row,col>::Matrix(const Matrix& m)
    {
    	copy(m);
    }
    
    template <size_t row, size_t col>
    Matrix<row,col>::~Matrix()
    {}
    
    template <size_t row, size_t col>
    Matrix<row,col>& Matrix<row,col>::operator=(const Matrix<row,col>& m)
    {
    	if(this != &m)
    	{
    		copy(m);
    	}
    	
    	return *this;
    }
    
    
    template <size_t row, size_t col>
    bool Matrix<row,col>::operator==(const Matrix<row,col>& m) const
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			if(matrix[i][j] != m.matrix[i][j])
    			{
    				return false;
    			}
    		}
    	}
    	
    	return true;
    }
    
    
    template <size_t row, size_t col>
    bool Matrix<row,col>::operator!=(const Matrix<row,col>& m) const
    {
    	return !( (*this) == m);
    }
    
    
    template <size_t row, size_t col>
    Matrix<row,col>& Matrix<row,col>::operator+=(const Matrix<row,col>& m)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] += m.matrix[i][j];
    		}
    	}
    	
    	return *this;
    }
    
    
    template <size_t row, size_t col>
    Matrix<row,col>& Matrix<row,col>::operator-=(const Matrix<row,col>& m)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] -= m.matrix[i][j];
    		}
    	}
    	
    	return *this;
    }
    
    
    template <size_t row, size_t col>
    Matrix<row,col> Matrix<row,col>::operator+(const Matrix<row,col>& m) const
    {
    	Matrix<row,col> tmp(*this);
    	
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			tmp.matrix[i][j] += m.matrix[i][j];
    		}
    	}
    	
    	return std::move(tmp);
    }
    
    
    template <size_t row, size_t col>
    Matrix<row,col> Matrix<row,col>::operator-(const Matrix<row,col>& m) const
    {
    	Matrix<row,col> tmp(*this);
    	
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			tmp.matrix[i][j] -= m.matrix[i][j];
    		}
    	}
    	
    	return std::move(tmp);
    }
    
    template <size_t row, size_t col>
    double& Matrix<row,col>::operator()(int i, int j)
    {
    	assert(i > 0 && i <= r);
      	assert(j > 0 && j <= c);
    
    	return matrix[i-1][j-1];
    }
    
    template <size_t row, size_t col>
    const double& Matrix<row,col>::operator()(int i, int j) const
    {
    	assert(i > 0 && i <= r);
      	assert(j > 0 && j <= c);
    
    	return matrix[i-1][j-1];
    }
    
    template <size_t row, size_t col>
    int Matrix<row,col>::get_row() const
    {
    	return r;
    }
    
    template <size_t row, size_t col>
    int Matrix<row,col>::get_col() const
    {
    	return c;
    }
    
    template <size_t row, size_t col>
    void Matrix<row,col>::set(double val)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] = val;
    		}
    	}
    }
    
    template <size_t row, size_t col>
    void Matrix<row,col>::copy(const Matrix& m)
    {
    	// r = m.r; c = m.c ???
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			matrix[i][j] = m.matrix[i][j];
    		}
    	}
    }
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  3. #3

    [C++] Classe Matrix (Matrici Matematiche)

    Ciao a tutti.
    Ho ricominciato da capo a scrivere la mia classe per rappresentare delle matrici. Ho deciso di utilizzare i templates e i puntatori (sono a conoscenza di std::vector, std::array, std::valarray, ... ma preferisco aprofondire l'argomento puntatori).

    Queste sono le prime semplici righe che ho scritto. Il codice compila ma... é corretto? :)

    codice:
    #ifndef MATRIX_H
    #define MATRIX_H
    
    #include <iostream>
    
    template <typename T = double>
    class Matrix
    {
    public:
    	Matrix(size_t, size_t);
    	virtual ~Matrix();
    	
    	void set(const T&);
    
    private:
    	T** data;
    	size_t row;
    	size_t col;
    };
    
    #endif
    
    template <typename T>
    Matrix<T>::Matrix(size_t r, size_t c)
    : row(r), col(c), data(new T*[row])
    {
    	for(int i(0); i < row; i++)
    	{
    		data[i] = new T[col];
    	}
    }
    
    template <typename T>
    void Matrix<T>::set(const T& d)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			data[i][j] = d;
    		}
    	}
    }
    
    template <typename T>
    Matrix<T>::~Matrix()
    {
    	for(int i(0); i < row; i++)
    	{
    		delete data[i];
    	}
    	
    	delete data;
    }
    Una domanda. Ho messo come tipo di default double prima della definizione della classe; devo farlo anche prima di ogni funzione, distruttore o costruttore?

    Grazie,
    R.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  4. #4
    Ho aggiunto un po' di funzioni alla mia clesse. In particolare non sono molto convinto della funzione copy (che verrà poi chiamata da operator= e dal costruttore di copia) e della funzione cancel (chiamata dal dostruttore e nella funzione copy). Potreste darmi qualche dritta per migliorare la classe? Grazie!

    codice:
    #ifndef MATRIX_H
    #define MATRIX_H
    
    #include <iostream>
    #include <stdexcept>
    
    template <typename T = double>
    class Matrix
    {
    public:
    	Matrix(size_t, size_t);
    	virtual ~Matrix();
    	
    	void set(const T&);
    	size_t get_row() const;
    	size_t get_col() const;
    	void print(std::ostream&) const;
    	
    	T& operator()(size_t, size_t);
    	const T& operator()(size_t, size_t) const;
    
    private:
    	T** data;
    	size_t row;
    	size_t col;
    	
    	void copy(const Matrix<T>&);
    	void cancel();
    };
    
    #endif
    
    template <typename T>
    Matrix<T>::Matrix(size_t r, size_t c)
    : row(r), col(c), data(new T*[r])
    {
    	for(int i(0); i < row; i++)
    	{
    		data[i] = new T[col];
    	}
    }
    
    template <typename T>
    Matrix<T>::~Matrix()
    {
    	cancel();
    }
    
    template <typename T>
    void Matrix<T>::set(const T& d)
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			data[i][j] = d;
    		}
    	}
    }
    
    template <typename T>
    size_t Matrix<T>::get_row() const
    {
    	return row;
    }
    
    template <typename T>
    size_t Matrix<T>::get_col() const
    {
    	return col;
    }
    
    template <typename T>
    void Matrix<T>::print(std::ostream& out) const
    {
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			out << data[i][j] << ' ';
    		}
    		
    		out << std::endl;
    	}
    }
    
    template <typename T>
    T& Matrix<T>::operator()(size_t i, size_t j)
    {
    	if( (i == 0) or (j == 0) or (i > row) or (j > col))
    	{
    		throw std::out_of_range("");
    	}
    	
    	return data[i-1][j-1];
    }
    
    template <typename T>
    const T& Matrix<T>::operator()(size_t i, size_t j) const
    {
    	if( (i == 0) or (j == 0) or (i > row) or (j > col))
    	{
    		throw std::out_of_range("");
    	}
    	
    	return data[i-1][j-1];
    }
    
    template <typename T>
    void Matrix<T>::copy(const Matrix<T>& m)
    {
    	row = m.row;
    	col = m.col;
    	
    	cancel();
    	
    	data = new T*[row];
    	for(int i(0); i < row; i++)
    	{
    		data[i] = new T[col];
    	}
    	
    	for(int i(0); i < row; i++)
    	{
    		for(int j(0); j < col; j++)
    		{
    			data[i][j] = m.data[i][j];
    		}
    	}
    }
    
    template <typename T>
    void Matrix<T>::cancel()
    {
    	for(int i(0); i < row; i++)
    	{
    		delete data[i];
    	}
    	
    	delete data;
    }
    
    template <typename T>
    std::ostream& operator<<(std::ostream& out, const Matrix<T>& m)
    {
    	m.print(out);
    	
    	return out;
    }
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  5. #5
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381

    Re: [C++] Classe Matrix (Matrici Matematiche)

    Originariamente inviato da RooccoXXI
    Il codice compila ma... é corretto?
    Alcune osservazioni.
    L'intera definizione di classe e delle funzioni va messo tra:
    #ifndef MATRIX_H
    #define MATRIX_H
    ed #endif
    non solo la definizione della classe.

    Le funzioni definite fuori dal corpo della classe vanno specificate inline.

    Definire il template in quel modo è scomodo, dato che per avere il tipo di default (il double) sei costretto a scrivere:
    codice:
    Matrix<> ntx
    senza avere nessun vantaggio particolare.

    Io abbandonerei il T** in favore del T* (simulando la matrice tramite un array). E' più semplice da gestire e non ha contro indicazioni.

    Nota a margine.
    Potresti sperimentare l'uso di un allocatore (in stile STL).
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  6. #6

    Re: Re: [C++] Classe Matrix (Matrici Matematiche)

    Originariamente inviato da shodan
    Alcune osservazioni.
    L'intera definizione di classe e delle funzioni va messo tra:

    non solo la definizione della classe.

    Le funzioni definite fuori dal corpo della classe vanno specificate inline.

    Definire il template in quel modo è scomodo, dato che per avere il tipo di default (il double) sei costretto a scrivere:
    codice:
    Matrix<> ntx
    senza avere nessun vantaggio particolare.

    Io abbandonerei il T** in favore del T* (simulando la matrice tramite un array). E' più semplice da gestire e non ha contro indicazioni.

    Nota a margine.
    Potresti sperimentare l'uso di un allocatore (in stile STL).
    Ti aspettavo!

    Ho messo solo l'interfaccia nelle direttive per il preprocessore perché normalmente é solo quella che si trova nei .h. In pratica é tutto il .h che deve stare tra le direttive (quindi nel caso dei template anche le definizioni)?

    Dici solamente di togliere il valore per default e basta? Effettivamente serve a poco... (Era più che altro per far vedere che la concezione era per i double).

    Perché andrebbero specificate inline le funzioni definite fuori dal corpo della classe? Non vanno definite inline solo funzioni corte a cui si fa riferimento estremamente spesso? O dici questo per aumentare l'efficienza, così il codice viene eseguito senza "balzi"?

    Dici di fare T*(new [row * col]) e poi riferirsi all'elemento (i,j) come [i * col + j]? È più efficente? Il problema é che voglio far partire gli indici da 1, quindi agni volta avrei due operazioni in più (le sottrazioni): [(i-1)* col + (j-1)]. Cambia l'efficienza o non é minimamente rilevante?

    Non avevo sentito parlare di un allocatore... Ho guradato un po' su C++ reference ma non ho ben capito come utilizzaro nel mio caso... Potresti farmi un breve esempio (anche stupido)?

    Grazie mille! ; )
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  7. #7
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381

    Re: Re: Re: [C++] Classe Matrix (Matrici Matematiche)

    Originariamente inviato da RooccoXXI
    Ti aspettavo!
    Ricaricato batterie, acqua e olio tutto a posto.

    Ho messo solo l'interfaccia nelle direttive per il preprocessore perché normalmente é solo quella che si trova nei .h. In pratica é tutto il .h che deve stare tra le direttive (quindi nel caso dei template anche le definizioni)?
    Si, perché anche le funzioni sono dei template e come tali vanno inserite nel .h
    Ricorda sempre che i template sono casi speciali.

    Dici solamente di togliere il valore per default e basta? Effettivamente serve a poco... (Era più che altro per far vedere che la concezione era per i double).
    Il che non impedisce al compilatore di istanziare il template con tutto il possibile.
    Se vuoi restringere il range dei tipi con cui la classe può essere istanziata, devi usare static_assert:
    Es.
    codice:
    #include <type_traits>
    template <typename T>
    class Matrix {
        // solo i tipi aritmetici possono istanziare la classe.
        static_assert(std::is_arithmetic<T>::value,"Matrix funziona solo con tipi di dato aritmetico. Es. char, int, double");
        etc...
    Perché andrebbero specificate inline le funzioni definite fuori dal corpo della classe? Non vanno definite inline solo funzioni corte a cui si fa riferimento estremamente spesso? O dici questo per aumentare l'efficienza, così il codice viene eseguito senza "balzi"?
    Niente di tutto questo. Semplicemente se includi il file in due diverse unità di compilazione il linker si lamenta perché hai violato la "one definition rule", cioè un unico punto di accesso per ogni singola specializzazione del template.
    Di questi tempi è solo il compilatore che decide cosa mettere inline e cosa no, anche se glielo dici (a meno di direttive specifiche di ogni compilatore).

    Dici di fare T*(new [row * col]) e poi riferirsi all'elemento (i,j) come [i * col + j]?
    Si.
    È più efficente?
    Cosa intendi per efficienza?
    Se intendi l'accesso al singolo elemento, il tempo è lo stesso.
    Se intendi: creazione in un colpo solo della matrice al posto di un doppio for, copia di ogni singolo elemento tramite memcpy() al posto di due cicli for, confrontare l'uguaglianza di due istanze di matrice (di pari righe e colonne) con un solo for al posto di due (o addirittura con memcmp()), direi di si.
    Il problema é che voglio far partire gli indici da 1, quindi agni volta avrei due operazioni in più (le sottrazioni): [(i-1)* col + (j-1)]. Cambia l'efficienza o non é minimamente rilevante?
    Non mi pare un gran cosa far abbaiare un gatto. Usa gli indici in maniera normale senza complicarti la vita per niente. E comunque sono interi, l'efficienza è la stessa.

    Non avevo sentito parlare di un allocatore... Ho guradato un po' su C++ reference ma non ho ben capito come utilizzaro nel mio caso... Potresti farmi un breve esempio (anche stupido)?
    Nella STL l'allocatore serve per cambiare il modo di fornire memoria alla struttura dati sottostante e svincolarla dall'accoppiata new/delete. Questo perché se il programmatore dedide di usare un allocatore più performate di quello standard, può farlo (allocare memoria è un'operazione pesante nel lungo periodo ed è questo motivo per cui ti ho suggerito di usare un T* al posto di un T**. Nel primo caso fai un'unica allocazione, nel secondo fai colonne + 1 allocazioni e questo comporta meno efficienza in fase di allocazione e deallocazione del puntatore). Le funzioni chiave sono: allocate() e deallocate().
    Per l'uso dai un'occhiata qui.
    http://www.codeproject.com/Articles/...-and-Implement
    In ogni caso è solo un'idea che si può applicare in seguito (ed eventualmente vedere perché è conveniente in certi casi).
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  8. #8

    Re: Re: Re: Re: [C++] Classe Matrix (Matrici Matematiche)

    Originariamente inviato da shodan
    Il che non impedisce al compilatore di istanziare il template con tutto il possibile.
    Se vuoi restringere il range dei tipi con cui la classe può essere istanziata, devi usare static_assert:
    Es.
    codice:
    #include <type_traits>
    template <typename T>
    class Matrix {
        // solo i tipi aritmetici possono istanziare la classe.
        static_assert(std::is_arithmetic<T>::value,"Matrix funziona solo con tipi di dato aritmetico. Es. char, int, double");
        etc...
    Interessante possibilità, ma in questo caso mi sembra davvero restrittiva. Questo perché se un eventuale utilizzatore (sempre io!xD) creasse una classe per rappresentare l'insieme numerico dei numeri razionali in forma esatta (frazionaria), dovrebbe poter utilizzare comunque la mia classe! . Ma se potesse, in qualche modo, specificare che la sua classe é un tipo aritmetico allora tutto cambia... Questa funzione viene invocata ad ogni invocazione del costruttore anche se si trova fuori da esso? E provoca un uscita forzata del programma nel caso std::is_arithmetic<T>::value é false?

    Niente di tutto questo. Semplicemente se includi il file in due diverse unità di compilazione il linker si lamenta perché hai violato la "one definition rule", cioè un unico punto di accesso per ogni singola specializzazione del template.
    Di questi tempi è solo il compilatore che decide cosa mettere inline e cosa no, anche se glielo dici (a meno di direttive specifiche di ogni compilatore).
    Qui, sinceramente, mi sono perso... Significa che se nel Makefile includo Matrix.h in due diversi main() e utilizzo la stessa specializzazione del template il compilatore si lamenta? Quindi tutte le funzioni inline?

    Cosa intendi per efficienza?
    Se intendi l'accesso al singolo elemento, il tempo è lo stesso.
    Se intendi: creazione in un colpo solo della matrice al posto di un doppio for, copia di ogni singolo elemento tramite memcpy() al posto di due cicli for, confrontare l'uguaglianza di due istanze di matrice (di pari righe e colonne) con un solo for al posto di due (o addirittura con memcmp()), direi di si.
    Effettivamente la diminuzione dei cicli for é un gran guadagno!

    Non mi pare un gran cosa far abbaiare un gatto. Usa gli indici in maniera normale senza complicarti la vita per niente. E comunque sono interi, l'efficienza è la stessa.
    Mi sembrava più matematico. Nei miei libri di fisica computazionale e analisi numerica spesso gli algoritmi partono da 1, perché lo 0 é l'indice delle condizioni ai limiti e non necessita di essere salvato nella matrice (perché la condizione al limite rimane prevalentemente invariata)...

    Quindi l'istruzione di accesso [i] é praticamente identica a [i-1] (nel senso che l'operazione i-1 non é rilevante)?

    Nella STL l'allocatore serve per cambiare il modo di fornire memoria alla struttura dati sottostante e svincolarla dall'accoppiata new/delete. Questo perché se il programmatore dedide di usare un allocatore più performate di quello standard, può farlo (allocare memoria è un'operazione pesante nel lungo periodo ed è questo motivo per cui ti ho suggerito di usare un T* al posto di un T**. Nel primo caso fai un'unica allocazione, nel secondo fai colonne + 1 allocazioni e questo comporta meno efficienza in fase di allocazione e deallocazione del puntatore). Le funzioni chiave sono: allocate() e deallocate().
    Per l'uso dai un'occhiata qui.
    http://www.codeproject.com/Articles/...-and-Implement
    In ogni caso è solo un'idea che si può applicare in seguito (ed eventualmente vedere perché è conveniente in certi casi).
    Questo sarebbe un argomento da approfondire, ma credo che ci voglia un po' di tempo! . Intendi che si può allocare memoria anche con istruzioni diverse da new e delete (più performanti)? Mi leggerò la pagina che hai linkato. Comunque se dici che passare da T* all'allocatore standard in un secondo momento (cosa comporta il passaggio?) non é difficile, credo di rimanere sul T*, più comprensibile (per il mio livello).

    Grazie,
    R.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  9. #9
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381

    Re: Re: Re: Re: Re: [C++] Classe Matrix (Matrici Matematiche)

    Originariamente inviato da RooccoXXI
    Interessante possibilità, ma in questo caso mi sembra davvero restrittiva. Questo perché se un eventuale utilizzatore (sempre io!xD) creasse una classe per rappresentare l'insieme numerico dei numeri razionali in forma esatta (frazionaria), dovrebbe poter utilizzare comunque la mia classe! . Ma se potesse, in qualche modo, specificare che la sua classe é un tipo aritmetico allora tutto cambia... Questa funzione viene invocata ad ogni invocazione del costruttore anche se si trova fuori da esso? E provoca un uscita forzata del programma nel caso std::is_arithmetic<T>::value é false?
    La condizione dello static_assert può essere composta, pertanto basta creare un traits per la classe che ti interessa controllare e sei a posto. Al di la di questo, static_assert è controllato in fase di compilazione e se la condizione non è superata, il compilatore mostra il messaggio in output. In questo modo non puoi compilare se il tipo che gli passi non supera la condizione (per esempio se istanzi Matrix con un tipo non aritmetico come double*).

    Qui, sinceramente, mi sono perso... Significa che se nel Makefile includo Matrix.h in due diversi main() e utilizzo la stessa specializzazione del template il compilatore si lamenta? Quindi tutte le funzioni inline?
    Se hai un main.cpp e altrofile.cpp entrambi che includono Matrix.h, il linker si lamenta perché trova le stesse funzioni in due diversi file .cpp e non sa chi richiamare. L'inline serve a toglierlo dall'imbarazzo.

    Mi sembrava più matematico. Nei miei libri di fisica computazionale e analisi numerica spesso gli algoritmi partono da 1, perché lo 0 é l'indice delle condizioni ai limiti e non necessita di essere salvato nella matrice (perché la condizione al limite rimane prevalentemente invariata)...
    E' solo una notazione. Quello che in matematica va da 1 a n, con limite 0, in C++ va da 0 a n-1 con limite -1 (visto che gli indici non possono essere negativi). Dopo un po' la traslazione viene automatica. Comunque tratta la cosa come più ti è congeniale, non è certo questo il problema.

    Quindi l'istruzione di accesso [i] é praticamente identica a [i-1] (nel senso che l'operazione i-1 non é rilevante)?
    Beh, ci impieghera un picosecondo in più, che in quelche migliaio di anni può farsi sentire.

    Intendi che si può allocare memoria anche con istruzioni diverse da new e delete (più performanti)? Comunque se dici che passare da T* all'allocatore standard in un secondo momento (cosa comporta il passaggio?) non é difficile, credo di rimanere sul T*, più comprensibile (per il mio livello).
    E' un modo diverso e più flessibile di gestire la politica di allocazione della memoria.
    All'interno dell'allocatore puoi decidere se usare new, malloc, altre funzioni di terze parti per la gestione dell'heap, usare una funzione per un numero prestabilito di elementi.
    L'argomento è vasto e come ho detto si può affrontare in seguito (se ti interessa).
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  10. #10

    Re: Re: Re: Re: Re: Re: [C++] Classe Matrix (Matrici Matematiche)

    Originariamente inviato da shodan
    La condizione dello static_assert può essere composta, pertanto basta creare un traits per la classe che ti interessa controllare e sei a posto. Al di la di questo, static_assert è controllato in fase di compilazione e se la condizione non è superata, il compilatore mostra il messaggio in output. In questo modo non puoi compilare se il tipo che gli passi non supera la condizione (per esempio se istanzi Matrix con un tipo non aritmetico come double*).


    Se hai un main.cpp e altrofile.cpp entrambi che includono Matrix.h, il linker si lamenta perché trova le stesse funzioni in due diversi file .cpp e non sa chi richiamare. L'inline serve a toglierlo dall'imbarazzo.


    E' solo una notazione. Quello che in matematica va da 1 a n, con limite 0, in C++ va da 0 a n-1 con limite -1 (visto che gli indici non possono essere negativi). Dopo un po' la traslazione viene automatica. Comunque tratta la cosa come più ti è congeniale, non è certo questo il problema.


    Beh, ci impieghera un picosecondo in più, che in quelche migliaio di anni può farsi sentire. :D


    E' un modo diverso e più flessibile di gestire la politica di allocazione della memoria.
    All'interno dell'allocatore puoi decidere se usare new, malloc, altre funzioni di terze parti per la gestione dell'heap, usare una funzione per un numero prestabilito di elementi.
    L'argomento è vasto e come ho detto si può affrontare in seguito (se ti interessa).
    Grazie mille per i preziosi consigli. Allego qui la classe messa a posto, se hai vogli di dare un'occhiata! (Sembra funzionare tutto, a parte la moltiplicazione, dove credo di aver fatto casino ad utilizzare l'array semplice...) ;). Non ho capito se inline devo scriverlo solo nel prototipo, solo prima della definizione o in tutti e due i casi...! Cosa manca? Cosa si può migliorare? ;)

    Grazie mille!!!
    R.

    codice:
    #ifndef MATRIX_H
    #define MATRIX_H
    
    #include <iostream>
    #include <initializer_list>
    #include <stdexcept>
    #include <utility>
    #include <type_traits>
    
    template <typename T>
    class Matrix
    {
    	static_assert(std::is_arithmetic<T>::value,"");
    
    public:
    	Matrix(size_t, size_t);
    	Matrix(size_t, size_t, const T&);
    	Matrix(const Matrix<T>&);
    	virtual ~Matrix();
    	
    	void set(const T&);
    	size_t get_row() const;
    	size_t get_col() const;
    	
    	void print(std::ostream&) const;
    	
    	Matrix<T>& operator=(const Matrix<T>&);
    	T& operator()(size_t, size_t);
    	T operator()(size_t, size_t) const;
    	bool operator==(const Matrix<T>&) const;
    	bool operator!=(const Matrix<T>&) const;
    	Matrix<T>& operator+=(const Matrix<T>&);
        Matrix<T>& operator-=(const Matrix<T>&);
        Matrix<T> operator+(const Matrix<T>&) const;
        Matrix<T> operator-(const Matrix<T>&) const;
        Matrix<T>& operator*=(const T& v);
        Matrix<T>& operator*=(const Matrix<T>&);
        Matrix<T> operator*(const Matrix<T>&) const;
    	
    private:
    	size_t row;
    	size_t col;
    	size_t dim;
    	T* data;
    	
    	void copy(const Matrix<T>&);
    };
    
    template <typename T>
    Matrix<T>::Matrix(size_t r, size_t c)
    : row(r), col(c), dim(r*c), data(new T[r*c])
    {}
    
    template <typename T>
    Matrix<T>::Matrix(size_t r, size_t c, const T& v)
    : row(r), col(c), dim(r*c), data(new T[r*c])
    {
    	set(v);
    }
    
    template <typename T>
    Matrix<T>::Matrix(const Matrix<T>& m)
    {
    	copy(m);
    }
    
    template <typename T>
    Matrix<T>::~Matrix()
    {
    	delete [] data;
    }
    
    template <typename T>
    void Matrix<T>::set(const T& v)
    {
    	for(size_t i(0); i < dim; i++)
    	{
    		data[i] = v;
    	}
    }
    
    template <typename T>
    size_t Matrix<T>::get_row() const
    {
    	return row;
    }
    
    template <typename T>
    size_t Matrix<T>::get_col() const
    {
    	return col;
    }
    
    template <typename T>
    void Matrix<T>::print(std::ostream& out) const
    {
    	for(size_t i(0); i < row; i++)
    	{
    		for(size_t j(0); j < col; j++)
    		{
    			out << data[col*i + j] << ' ';
    		}
    		
    		out << std::endl;
    	}
    }
    
    template <typename T>
    Matrix<T>& Matrix<T>::operator=(const Matrix<T>& m)
    {
    	if(this != &m)
    	{
    		delete [] data;
    		copy(m);
    	}
    	
    	return *this;
    }
    
    template <typename T>
    T& Matrix<T>::operator()(size_t i, size_t j)
    {
    	if( (i == 0) or (j == 0) or (i > row) or (j > col))
    	{
    		throw std::out_of_range("");
    	}
    
    	return data[col*(i-1) + (j-1)];
    }
    
    template <typename T>
    T Matrix<T>::operator()(size_t i, size_t j) const
    {
    	if( (i == 0) or (j == 0) or (i > row) or (j > col))
    	{
    		throw std::out_of_range("");
    	}
    
    	return data[col*(i-1) + (j-1)];
    }
    
    template <typename T>
    bool Matrix<T>::operator==(const Matrix<T>& m) const
    {
    	if( (row != m.row) or (col != m.col) )
    	{
    		return false;
    	}
    	
    	for(size_t i(0); i < row; i++)
    	{
    		if(data[i] != m.data[i])
    		{
    			return false;
    		}
    	}
    	
    	return true;
    }
    
    template <typename T>
    bool Matrix<T>::operator!=(const Matrix<T>& m) const
    {
    	return !((*this) == m);
    }
    
    template <typename T>
    Matrix<T>& Matrix<T>::operator+=(const Matrix<T>& m)
    {
    	if( (row != m.row) or (col != m.col) )
    	{
    		throw std::range_error("");
    	}
    
    	for(size_t i(0); i < dim; i++)
    	{
    		data[i] += m.data[i];
    	}
    	
    	return *this;
    }
    
    template <typename T>
    Matrix<T>& Matrix<T>::operator-=(const Matrix<T>& m)
    {
    	if( (row != m.row) or (col != m.col) )
    	{
    		throw std::range_error("");
    	}
    
    	for(size_t i(0); i < dim; i++)
    	{
    		data[i] -= m.data[i];
    	}
    	
    	return *this;
    }
    
    template <typename T>
    Matrix<T> Matrix<T>::operator+(const Matrix<T>& m) const
    {
    	if( (row != m.row) or (col != m.col) )
    	{
    		throw std::range_error("");
    	}
    
    	Matrix<T> tmp(*this);
    	
    	for(size_t i(0); i < dim; i++)
    	{
    		tmp.data[i] += m.data[i];
    	}
    	
    	return std::move(tmp);
    }
    
    template <typename T>
    Matrix<T> Matrix<T>::operator-(const Matrix<T>& m) const
    {
    	if( (row != m.row) or (col != m.col) )
    	{
    		throw std::range_error("");
    	}
    
    	Matrix<T> tmp(*this);
    	
    	for(size_t i(0); i < dim; i++)
    	{
    		tmp.data[i] -= m.data[i];
    	}
    	
    	return std::move(tmp);
    }
    
    template <typename T>
    Matrix<T>& Matrix<T>::operator*=(const T& v)
    {
    	for(size_t i(0); i < dim; i++)
    	{
    		data[i] *= v;
    	}
    	
    	return *this;
    }
    
    template <typename T>
    Matrix<T>& Matrix<T>::operator*=(const Matrix<T>& m)
    {
    	if( col != m.row )
    	{
    		throw std::range_error("");
    	}
    	
    	Matrix<T> tmp(*this);
    	
    	col = m.col;
    	delete [] data;
    	data = new T[row*col];
    	
    	for(size_t i(0); i < row; i++)
    	{
    		for(size_t j(0); j < m.row; j++)
    		{
    			for(size_t k(0); k < m.col; k++ )
    			{
    				data[col*i + j] += tmp.data[tmp.col*i + k] * m.data[m.col*k + j];
    			}
    		}
    	}
    	
    	return *this;
    }
    
    template <typename T>
    Matrix<T> Matrix<T>::operator*(const Matrix<T>& m) const
    {
    	if( col != m.row )
    	{
    		throw std::range_error("");
    	}
    	
    	Matrix<T> tmp(*this);
    	
    	return std::move(tmp *= m);
    }
    
    template <typename T>
    void Matrix<T>::copy(const Matrix<T>& m)
    {
    	row = m.row;
    	col = m.col;
    	dim = m.dim;
    	
    	data = new T[dim];
    	
    	for(size_t i(0); i < dim; i++)
    	{
    		data[i] = m.data[i];
    	}
    }
    
    template <typename T>
    Matrix<T> operator*(const T& v, const Matrix<T>& m)
    {
    	Matrix<T> tmp(m);
    
    	tmp *= v;
    	
    	return std::move(tmp);
    }
    
    template <typename T>
    Matrix<T> operator*(const Matrix<T>& m, const T& v)
    {
    	return std::move(v * m);
    }
    
    template <typename T>
    std::ostream& operator<<(std::ostream& out, const Matrix<T>& m)
    {
    	m.print(out);
    	
    	return out;
    }
    
    #endif
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.