Section 6 - Multidimensional arrays

-All the same rule for 1D arrays hold here: can't print the whole array with cout <<, unless you overload <<; can't assign one array to another, unless you overload =; etc.

-There is no limit to the number of dimensions defined

-Two-dimensional arrays

-Think of these as a matrix or a grid

-for an NxM array, each row is numbered 0 to N-1 and each column is numbered from 0 to M-1

-e.g. a 5x4 matrix:
0 12 3
012 3 2354
16 5 8776
232 45 5437
37 8 465
43 45 5542

-to declare such a structure in C++:

const int

NumRows = 5,

NumCols = 4;

int

Matrix[NumRows][NumCols];

-and to access any particular element:

Matrix[0][0] //12

Matrix[4][3] //42

etc.

-each of these is a single integer!

-extended example:

-suppose we had to track daily temperatures for 4 weeks - w/o 2D array, we'd need 4 arrays of 7 days each

-instead, we can do this:

float

Temps[4][7];

-with this definition, we can process a month's worth of data at once:

-getting the temps from a file:

for (r=0; r<4; r++)

for(c=0; c<7; c++)

Instream >> Temps[r][c];

-printing out in four rows:

for (r=0; r<4; r++)

{

for (c=0; c<7; c++)

Outstream << Temps[r][c];

Outstream << endl;

}

-To better facilitate readability and ease of use, we can declare a data type of our array type, using the typedef specifier:

typedef float TempMatrix[4][7];

TempMatrix //declare an array of type tempmatrix

Temps;

-Then, to pass the variable to a function, you don't need the [ ]'s, since the data type IS an array:

void PrintTemps(TempMatrix Temps; fstream& out)

{

etc.

}

-We could further add to readability by using enumerated types as the indices:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat, NumDays};

enum Weeks {Week1,Week2,Week3,Week4,NumWeeks};

typedef float TempMatrix[NumWeeks][NumDays];

TempMatrix //declare an array of type tempmatrix

Temps;

. . .

Temps[Week1][Sun] //much clearer than Temps[0][0]

-of course, you'd have to create functions to work with the enumerated types, for incrementing, etc. - but in the long run, it makes the program much better

-Suppose we wanted to find the average temperature for each week- we'd want a function to do this, but we'd have to pass the entire 2D array, and the week number to average, or we'd have to pass 7 pieces of the array: Temps[Week1][Sun], Temps[Week1][Mon], etc.
UGH!
-It would be nicer if we could just pass a week as a whole entity, but the array is not defined that way - BUT WE CAN DEFINE IT THAT WAY!

-Think of defining the 2D array as a 4 element array (4 weeks) and each element is an array (7 days) - an array of arrays!

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat, NumDays};

//now define a data type of an array of 7 days

typedef float TempList[NumDays];

enum Weeks {Week1,Week2,Week3,Week4,NumWeeks};

//now define a data type of an array of 4 weeks

typedef TempList TempMatrix[NumWeeks];

//now declare a variable of type TempMatrix (an array of arrays)

TempMatrix

Temps;

-This implies that the following:

Temps[Week1]

IS a 7 element array! It is a variable of type Weeks!

-so our function to average the temperatures for one week looks like this:

double WeekAvg(Weeks Aweek)

{

double sum=0.0;

for (Day d=Sun; d<=Sat; d++) //overloaded ++

Sum += Aweek[d];

return Sum/NumDays;

}//end function

and the call looks like this:

WkAvg = WeekAvg(Temps[Week1]);

-With this type of definition, you could even swap rows (given swap overload definition):

Swap(Temps[Week1],Temps[Week2]);

-What would swap definition look like??

-What if we wanted to swap columns?? (given an array of arrays definition)

void SwapColums(TempMatrix Mat, Day d1, Day d2)

//pass in whole array and column values (days) of columns to swap

{

for (Week w=Week1; w < NumWeeks; w++) //overloaded ++
Swap(Mat[w][d1], Mat[w,d2]); //real # swap!

}

-A similar algorithm would be needed to swap rows if matrix had not been defined as array of arrays

-When program is modeling data which uses a matrix row as an entity, it may be beneficial to use the "array of arrays" definition. Otherwise, the simple definition is better, and easier to understand

-reviewing the difference in declaration (w/o enumerated types):

-this is 'simple' matrix:

typedef double Matrix[NumRows][NumCols] //constants defined elsewhere

Matrix

Mat;

-this is the array of arrays:

typedef double Row[NumCols]; //an array of type double

typedef Row Matrix[NumRows]; //an array of type Row

Matrix
Mat;

-practice (using the above 'simple' type):

  1. -Read in numbers from file and fill matrix (don't read too many!)
  2. -Compute the product of the row sums (i.e., sum the rows and multiply these sums)
  3. -Compute the sum of the row products (i.e., multiply the row values and sum these products)
  4. -Compute the product of the column sums
  5. -Compute the sum of the column products
  6. -Print in matrix (row by row) format
  7. -Print the Nth row or column of matrix (given N)
  8. -Print the left (downward) or right (upward) diagonal of a square matrix

(Using the array of arrays declaration, #s 2 and 3 could be made simpler by using a function to compute the sum of the row elements and the product of the row elements, passing an entire row to these functions)

-Template for #2 and #3:

product = 1; //sum=0 for #3

for (r=0; r<NumRows; r++)
{
sum = 0; //product=1 for #3

for (c=0; c<NumCols; c++)

sum += Mat[r][c]; //product*=Mat[r][c] for #3

product *= sum; //sum += product for #3

}

- work out others on own

-Initializing 2D arrays - just like single-D arrays:

int

Matrix[4][3] = {9,8,7,6,5,4,3,2,1,0,1,2}; //12 values (4X3)

-to make it clearer, can show rows/columns:

int

Matrix[4][3] = {{9,8,7},
(6,5,4},
{3,2,1},
{0,1,2}};

-As with 1D arrays, initializing a numeric array with fewer than the required elements will result in the remaining elements defaulting to 0

-Multidimensional arrays

-you can define any number of dimensions of arrays - whatever is required of the problem

-for example, our temperature matrix, suppose we wanted to track a year's worth of temperatures (per month) - could declare 12 separate matrices, one per month, but that is very clunky - instead, create a 3D array (i.e., an array of matrices!)

-e.g.:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat, NumDays};

enum Weeks {Week1,Week2,Week3,Week4,NumWeeks};

enum Months {Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,NumMonths};

//now define a data type of an array of 7 days

typedef float TempList[NumDays];

//now define a data type of an array of 4 weeks

typedef TempList TempMatrix[NumWeeks];

//now define a data type of an array of 12 months

typedef TempMatrix Year[NumMonths];

//now declare a variable of type Year (an array of arrays of arrays)

Year

Temps;

//ALTERNATE DEFINITION, THAT DOESN'T USE ARRAY-OF-ARRAYS STRUCTURE:

float

Temps [NumMonths][NumWeeks][NumDays];

//but this would be awkward to pass as parameter

To access a given day:

Temps[Jan][Week1][Mon]

Temps[Dec][Week4][Sat]

Function prototype (using array of array type):

void PrintArray(Year Temps);

Comparing two days' temperatures:

if (Temps[Jan][Week2][Tue] > Temps[Dec][Week2][Wed])

etc.

Here's the function to fill entire array:

void GetTemps(Year Temps, fstream& Infile)

{

for (Months M=Jan; M<=Dec; M++) //++ overloaded for type Months

for(Weeks W=Week1; W<=Week4; W++) //++ overloaded for type Weeks

for(Days D=Sun; D<=Sat; D++) //++ overloaded for type Days

Infile >> Temps[M][W][D]; //read in one real # into array

}//end function

-Other practice routines for 3D arrays:

-Print out entire array in "nice" format
-Find average yearly temp
-Find average monthly temp

-Find average daily temp

-Find highest or lowest daily temp

-4D arrays

-declaring 4D array (not array of array format)
typedef float Time[40][10][10][10];

Time

Stats;

Stats[p][q][r][s] //one real number