<Previous Lesson

Introduction to Programming

Next Lesson>

Lesson#43

Lesson 43

Summary

Programming Exercise - Matrices
Design Recipe
Problem Analysis
Design Issues and Class Interface

Programming Exercise - Matrices


Mathematics is a good domain to develop different classes and programs. For example,
solutions for Complex numbers, Matrices and Quadratic Equations can be sought for
developing our own classes. In this lecture, we will take a problem to manipulate and
perform different operations on Matrices. Matrices are used in lot of real world problems.
We will perform problem analysis, design and implementation.
Let’s take a look at analysis and design phases first by using our design recipe.
Design Recipe

Firstly we do analysis and try to come up with a problem statement. Express its essence,
abstractly and with examples. After describing the problems in few sentences, we try to
formulate the problem with examples. It is emphasized to pay attention to the details. We
do analysis of the data structures to be used in the program and choose the best fit to the
program requirements. The code is written to implement the program. After
implementation is completed, we do its testing to verify that it is behaving properly in all
scenarios. If any bugs are found, they are fixed. This cycle of testing and bug fixing
continues until the program is working perfectly without any problem.
We are going to write a program to manage operations on Matrices.
Page 553
At the start of the problem analysis phase, let’s try to understand the problem domain
first.

Problem Analysis


A matrix is nothing but a two-dimensional array of numbers. It is normally represented in
rows and columns. A matrix is represented as:
It is a matrix A with 3 rows and 4 columns. So order of the matrix is 3 * 4.
Before going further, let’s consider what are the operations normally performed on
matrices.
- A matrix is added to another matrix.
- A scalar value (an ordinary number) is added to a matrix.
- A matrix is subtracted from another matrix.
- A scalar number is subtracted from a matrix.
- A matrix is multiplied with another matrix.
- A scalar number is multiplied with a matrix.
- A matrix is divided by a scalar.
- A matrix is transposed.
Now, we will define what these operations are and if there are any restrictions on
matrices performing these operations.
The sum or addition of two matrices of the same order is found by adding the
corresponding elements of the two matrices. If A and B are two matrices of order m * n to
be added then their resultant matrix will also have the same order m * n.
A
ij + Bij
Where i varies from 1 to m (max number of rows) and j varies from 1 to n (max number
of cols).
Clearly, there is a restriction on the matrices performing this addition operation that they
should have same numbers of rows and columns, in other words their order should be the
same.
There is another operation of addition of scalar number to a matrix. In this operation, a
number is added to all elements of the matrix.
Subtraction operation works in the same fashion that two matrices of the same order takes
part in this operation and resultant matrix with similar order is obtained by subtracting
each element of one matrix from the corresponding element of other matrix. For example,
see the subtraction operation and assignment below:
C
ij = Aij - Bij
1 2 3 4
5 6 7 8
9 10 11 12
A =


-2 -4 -5
-2 2 0
0 0 10 = -
1 2 3
5 6 7
9 10 11
3 6 8
7 4 7
9 10 1
Page 554
Not to confuse your understanding with assignment in computer programs, the resultant
matrix is put on the left of assignment operator otherwise in Mathematics it is located on
the right.
Each element of matrix B is subtracted from the corresponding element of the matrix A
and the resultant goes to matrix C. C will have the same number of rows and columns as
A
and B.
Similar to the addition, there is another operation for subtracting a scalar from a matrix.
In this case, a number is subtracted from each element of the matrix.
For Division of a matrix by a scalar, the scalar number divides each element of the
matrix. Let x be a scalar number and A be a matrix then division is represented as:
C
ij = Aij / x
Each element of matrix A is divided by the number x to produce the corresponding
number in the resultant matrix C. For example, A11 (element in first row and first column
of matrix A) is divided by the scalar number x to provide C11 (element in first row and
first column of matrix C).
The multiplication operation is bit complicated as compared to the above discussed
operations. We will discuss simple case first, when a scalar is multiplied by a matrix.
Suppose, this time we want to multiply the scalar x with the matrix A as:
C
ij = x * Aij
Each element of matrix A is multiplied with the scalar x and the resultant number is put in
the corresponding location inside the matrix C.
Now, we will see how a matrix is multiplied with another matrix. Firstly, there is a
restriction on order of the matrices involved in this operation. The number of columns of
the first matrix should be equal to the number of rows of the second matrix.
Two matrices are multiplied in the following manner:
We take the first row of first matrix and multiply it with the first column of the second
matrix. The multiplication is done in such a way that the first element of the row is
multiplied with the first element of the column, second element is multiplied with the
second element and so on. The results of all these multiplication operations are added to
produce one number. The resultant number is placed at the corresponding position (i.e. 1st
row 1st col in this case) in the resultant matrix.
Further the same first row is multiplied with the second column of the second matrix and
the resultant number is placed at intersecting position of first row and second column in
the resultant matrix. This process goes on till the last column of the second matrix.
Page 555
Then comes the second row of first matrix and whole operation is repeated for this row,
this row is multiplied with all the columns of the second matrix. This process goes on till
the last row of the first matrix.
Note the resultant matrix is put on the left of the =. In Mathematics, this is put on right
but not to confuse your understanding with assignment concept in computer programs, it
is put on left.
If a matrix with order m rows, n columns is multiplied with another matrix of n rows and
p
columns then the resultant matrix will have m rows and p columns. In the above
diagram, the first matrix has two rows and second matrix has two columns, therefore, the
resultant matrix has two rows and two columns.
Now comes the last operation, we are thinking of implementing i.e. Transpose of a
matrix. Transpose of a matrix is obtained by interchanging its rows and columns. How do
we interchange rows and columns for transposing the matrix? We take the first row of the
matrix and write it as a first column of the new matrix. The second row of the original
matrix is written as second column of the new matrix and similarly the last row of the
original matrix is written as last column of the new matrix. At the end of this operation,
when all rows of the original matrix are finished, we have new matrix as transpose of the
original matrix. There is no change in the size (order or number of rows and cols of a
matrix) of the transposed matrix when the original matrix is a square matrix. But when
the original matrix is not a square matrix, there is a change in the order of the transposed
matrix. The number of rows of the original matrix becomes the number of columns of the
transposed matrix and the number of columns of the original matrix becomes the number
of rows of the transposed matrix.
Until now in this problem analysis phase, we have analyzed the problem in order to
understand what are the matrices and what are their operations to be implemented. Now
at the next stage, we try to determine the followings:
- What are the constants to be used in our class?
- What are going to be the data structures to cater to the different sized matrices?
- How much memory is required and how it will be allocated?
(1)(2)+(2)(1) (1)(4)+(2)(2)
(5)(2)+(6)(1) (5)(4)+(6)(2)
1 2 *
5 6
2 4
1 2
1 2 3
5 6 7
9 1 0 11
1 5 9
2 6 10
3 7 11

Page 556
- What is going to be the interface of the class?

Design Issues and Class Interface


We want to specify the size of the matrix at creation time and allocate the memory for
that. So we don’t see any use of constants inside our class named Matrix.
The size of the memory to be allocated is not going to be huge, as we are not catering to
the very huge sized matrices. Therefore, the memory for a matrix is going to be allocated
dynamically bluntly after the size of the matrix is specified in terms of rows and columns
without worrying about the size of the matrix.
For the interface of our Matrix class, we will declare a constructor that will accept integer
number of rows and columns of the matrix to be created as parameters.
Matrix ( int rows, int cols ) ;

The constructor function will be doing the memory allocation for the matrix.
As part of the interface, we will declare a display function inside our Matrix class that
will display the elements on the screen.
void display ( Matrix & );

To perform already discussed different operations on matrices, we need to overload
operators. For example to perform addition of two matrices, + operator will be
overloaded as a member function of the Matrix class. The + operator function will be
called for the Matrix object on the left of the + and the Matrix object on the right to it
will be passed as a parameter to it. This function will add the corresponding elements of
the both matrices and returns the resultant back.
Matrix operator + ( Matrix & ) const;

The same thing applies to the subtraction operation of two matrices. – operator function
will be overloaded for that as a member function of the Matrix class.
Matrix operator - ( Matrix & ) const;

The situation changes a bit, when we want to write the functions to cater to different
operations where both the operands are not matrix objects rather one of them is scalar.
For example, when we want to do the following operation:
A + x ;

Where A is a matrix and x is a scalar.
Then we write a member function that accepts a scalar number as a parameter instead of a
Matrix object.
Page 557
Matrix operator + ( Scalar ) const;

The Scalar can be an int, double or float, that we will cover later.
But the situation is more different, when we want to perform the scalar addition operation
in the following manner:
x + A ;

By now we should be clear that member function cannot be written to handle this
operation because there is a scalar number on the left of +. Therefore, we need to write a
friend operator
function for this type of operation. The friend functions are non-members
and therefore, defined outside of the class.
friend Matrix operator + ( Scalar , Matrix & ) ;

Similarly, when a scalar is subtracted from a Matrix object like the following:
A - x ;

A member function is written to cater to this operation.
Matrix operator - ( Scalar ) const;

But again, when a matrix is subtracted from a scalar number:
x - A ;

Then we have to write a friend operator to handle this operation.
friend Matrix operator - ( Scalar , Matrix & ) ;

In order handle the multiplication operations of two Matrix objects like the following:
A * B ;

A member operator * function is defined.
Matrix operator * ( const Matrix & ) ;

This operator is called for the Matrix object on the left of * and the object on the right is
passed as an argument. The function multiplies both the matrices and returns the resultant
matrix.
When a scalar is multiplied with a scalar like:
A * x ;

Page 558
The following member operator * handles this:
Matrix operator * ( Scalar ) const;

But for operation like the following:
x * A;

following friend operator function is written:
friend Matrix operator * ( const Scalar , const Matrix & ) ;

For division operation like the following:
A / x;

A member operator / is overloaded as:
Matrix operator / ( const Scalar );

Now we will talk about transpose of a matrix. For this operation, we will write a member
function transpose that will transpose the original matrix.
Matrix & transpose(void) ;

Now we are left with few more things to cover to complete the rudimentary interface of
our class Matrix.
Operators += and -= are overloaded as member operators. These composite operators
use the assignment operator ( = ).
We will also overload stream insertion and extraction operators as friend functions to our
Matrix
class as follows:
friend ostream & operator << ( ostream & , Matrix & ) ;

friend istream & operator >> ( istream & , Matrix & ) ;

So here is how we declare our Matrix class. The interface of the class is the public
methods of the class. Here is one important point to understand that what we are
concerned about here is the class interface and not about the program interface to the user
of the program. A programmer can develop user interface by writing his/her code while
using the class interface.
/* Declaration of the Matrix class. This class is containing the double type elements */
Page 559
class Matrix
{
private:
int numRows, numCols;
double **elements;
public:
Matrix(int=0, int=0); // default constructor
Matrix(const Matrix & ); // copy constructor
~Matrix(); // Destructor
int getRows(void) const; // Utility fn, returns no. of rows
int getCols(void) const; // Utility fn, returns no. of columns
const Matrix & input(istream &is = cin); // Read matrix from istream
const Matrix & input(ifstream &is); // Read matrix from istream
void output(ofstream &os) const; // Utility fn, prints matrix with graphics
void output(ostream &os = cout) const; // Utility fn, prints matrix with graphics
const Matrix& transpose(void); // Transpose the matrix and return a ref
const Matrix & operator = (const Matrix &m); // Assignment operator
Matrix operator+( Matrix &m) const; // Member op + for A+B; returns matrix
Matrix operator + (double d) const;
const Matrix & operator += (Matrix &m);
friend Matrix operator + (double d, Matrix &m);
Matrix operator-( Matrix & m) const; // Member op + for A+B; returns matrix
Matrix operator - (double d) const;
const Matrix & operator -= (Matrix &m);
friend Matrix operator - (double d, Matrix& m);
Matrix operator*(const Matrix & m);
Matrix operator * (double d) const;
friend Matrix operator * (const double d, const Matrix& m);
Matrix operator/(const double d);
friend ostream & operator << ( ostream & , Matrix & );
friend istream & operator >> ( istream & , Matrix & );
friend ofstream & operator << ( ofstream & , Matrix & );
friend ifstream & operator >> ( ifstream & , Matrix & );
void display( ) ;
};
In the above declarations, we should note how we are passing and returning Matrix
objects. We are passing and returning the Matrix objects by reference because passing the
Page 560
Matrix objects by value will be a overhead that will affect performance and more
memory will be allocated and de-allocated on stack.
Notice that we are doing dynamic memory allocation inside the constructor of the class.
You must be remembering that wherever the dynamic memory allocation is made, it has
to be freed explicitly. To de-allocate the memory, we will write code inside the destructor
of the class Matrix. The other consideration when we are allocating memory on free store
from within constructor is that the default assignment operator will not work here.
Remember, the default assignment operator makes shallow copy of the object members,
therefore, we will have to write our own assignment operator ( = ) in order to make deep
copy
of the object data members. Remember that a copy constructor is called when a new
Matrix
object is initialized and constructed based on an already existent Matrix object.
Therefore, we have to write our own copy constructor in order to make deep copy of the
object data members.
There is one very important point to mention about this class Matrix. A Matrix can be
composed of ints, floats or doubles as their elements. Instead of handling these data types
separately, we can write Matrix class as a template class and write code once for all
native data types. While writing this template class, the better approach to write will be,
to go with a simple data type (e.g. double) first to write a Matrix class and then extend it
to a template class later. Another thing that can be templatized in the Matrix class is the
Scalar
number. Actually, this Scalar number can be an int, float or double; therefore, we
may also use a template for this.
We have to perform certain checks and make decisions inside the implementation of
member functions. For example, while writing the division operator member function, we
will check against the number that it should be non-zero. Before adding two matrices, we
will check for their number of rows and columns to be equal. Also in this exercise, we
have declared only one class Matrix to manipulate matrices. There are alternate
approaches to this. For example, we could declare a Row class first and then contain
multiple objects (same in number as number of rows required for the matrix object) of
Row
class inside the Matrix class making a matrix of a certain size. To make it simple,
we have selected to manage matrices using only one class Matrix. The objective here is to
practice the already studied programming constructs as much as possible.

<Previous Lesson

Introduction to Programming

Next Lesson>

Home

Lesson Plan

Topics

Go to Top

Copyright © 2008-2013 zainbooks All Rights Reserved
Next Lesson
Previous Lesson
Lesson Plan
Topics
Home
Go to Top