Introduction

In economic applications it's not uncommon to have
matrices with a large number of zero-valued elements and, because C/C++ stores
zeros in the same way it stores any other numeric value, these elements can use
memory space unnecessarily and can sometimes require extra computing time.
Examples of applications that use sparse matrices: graph stored as adjacency
matrices, optimization problems using linear algebra and sparse linear equation
systems.

Sparse matrices provide a way to store data that has a
large percentage of zero elements more efficiently. While full matrices
internally store every element in memory regardless of value, sparse matrices
store only the nonzero elements and their position. Using sparse matrices can
significantly reduce the amount of memory required for data storage.

There are two main advantages of using sparse matrices:

·greatly reduced memory requirements

·faster computations by eliminating operations on
zero elements

Because sparse matrices store both element values and
position for non-zero elements, they require more storage space if number of
such elements isn’t much smaller than zero elements. Another drawback of using
a sparse matrix is that it doesn’t provide direct access to elements. Basic
operations are also more difficult to implement compared to normal matrices.

These tradeoffs must be well considered when deciding
whether to use a normal or a sparse matrix. Usually sparse matrices are used
when dealing with large volumes of data that contain between 0.15% and 3%
non-zero elements.

Storage

When storing a sparse matrix we need to consider two kinds
of information:

the
values for non-zero elements

positioning
of these element in the matrix

There are many ways to store a sparse matrix in memory:

a)Using
three arrays for line indices, column indices and values. The first two arrays
can be replaced by a single array containing the index of the elements in the linearized
matrix.

b)By
grouping the indices and value triplets in a structure and storing them into an
array or a linked list.

c)Using
a bit array for marking the positioning of the non-zero elements in the
linearized matrix and a separate array for values.

In the following sections we’ll use the first form (we’ll
use three dynamically allocated arrays for storing line indices, column indices
and values). For easier manipulation all the information related to a matrix is
grouped into a C structure:

struct MatriceRara

{

//
vectorii pentru stocarea liniei/coloanei si valorii

int
*Linie, *Coloana, *Valoare;

//
numarul de elemente nenule din matrice

int
nrElemente, nrLinii, nrColoane;

};

Conversions

The conversions between normal matrices and sparse matrices
and vice versa are straightforward:

// Conversie matrice normala -> matrice rara

MatriceRara Conversie(int **mat, int m, int n)

{

MatriceRara
matR;

//
determinarea numarului de elemente

int
i, j, nrElemente = 0, indiceMatR = 0;

for
(i = 0; i < m; i++)

for
(j = 0; j < n; j++)

if
(mat[i][j] != 0)

nrElemente++;

//
initializare matrice rara

matR.Linie
= new int[nrElemente];

matR.Coloana
= new int[nrElemente];

matR.Valoare
= new int[nrElemente];

matR.nrElemente
= nrElemente;

matR.nrLinii
= m;

matR.nrColoane
= n;

//
copierea valorilor nenule

for
(i = 0; i < m; i++)

for
(j = 0; j < n; j++)

if
(mat[i][j] != 0)

{

matR.Linie[indiceMatR]
= i;

matR.Coloana[indiceMatR]
= j;

matR.Valoare[indiceMatR]
= mat[i][j];

indiceMatR++;

}

return
matR;

}

// Conversie matrice rara -> matrice normala

int ** Conversie(MatriceRara& matR)

{

//
declarare si alocare matrice

int
**mat;

mat =
new int*[matR.nrLinii];

for
(int i = 0; i < matR.nrLinii; i++)

{

mat[i]
= new int[matR.nrColoane];

for
(int j = 0; j < matR.nrColoane; j++)

mat[i][j]
= 0;

}

//
copiere elemente nenule

for
(int i = 0; i < matR.nrElemente; i++)

mat[matR.Linie[i]][matR.Coloana[i]]
= matR.Valoare[i];

//
returnare rezultat

return
mat;

}

Basic operations

One way to implement matrix operations on sparse matrices
is to simulate direct access to elements using Get/Set functions and use the
traditional algorithms. This is the simplest method but has the drawback that
is very slow because we must consider all elements (even non-zero ones) and use
the sequential access functions that need O(nrZ) time to complete.

A better solution is to develop specialized algorithms
that use the particularities of the sparse matrices to provide a fast execution
time. In order to speed up the operations we’ll assume that elements inside the
sparse matrix are sorted according to (line, column); all operations presented
here preserve this property on the matrices.

Transpose

The algorithm to compute the transpose of a sparse matrix is
very simple. All we need to do is swap the columns and line indices and sort
the resulted arrays in order to preserve the mentioned property:

Addition

determine
the number of elements in the resulted matrix and allocate memory for them

compute
the value for all elements and store them in the resulted matrix

Because the matrices are sorted by (line, column) we can
do only one pass to determine the number of elements and to compute the values.
In the process we take care to eliminate zero elements in the result.

Exercises

1.Write
a function to eliminate all elements with values less than a threshold from a
sparse matrix.

2.Write
functions to read a sparse matrix from the console and to write the result in
both forms (sparse and normal).

3.Write
a function to multiply a sparse matrix with a constant. Use this function in
conjunction with the addition to simulate sparse matrix subtraction.

4.Write
a function to multiply a sparse matrix by a line vector.

5.Give
a solution to eliminate the necessity of the first pass in the matrix
multiplication algorithm.

6.An
airline company operates direct flights between n cities. Write an application to determine the minimum number of
flight segments between any two cities.

Solution:

We model the problem as an undirected graph with cities
represented as vertexes and flight segments as edges and we store the graph as
an adjacency matrix A (using a sparse matrix). In this case Ak will contain all flight routes of
length k between cities.