Вы находитесь на странице: 1из 55

MPR211 Notes Part 2

Schalk Kok

2005

Contents
1 Numerical integration 2
1.1 Numerical integration using a while loop . . . . . . . . . . . . . . . . . . . 5

2 Vectors and matrices 9


2.1 Defining a matrix using the input statement . . . . . . . . . . . . . . . . . 11
2.2 Matrix manipulations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Example: Norm of a vector . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4 Example: Matrix-vector multiplication . . . . . . . . . . . . . . . . . . . . 14
2.5 Example: Plots of functions of a single variable . . . . . . . . . . . . . . . 15

3 Revisiting previous programs 16


3.1 Taylor series using vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2 Numerical integration using vectors . . . . . . . . . . . . . . . . . . . . . . 18

4 Sorting algorithms 21
4.1 Bubble sort algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

5 Gauss elimination 26
5.1 Gauss elimination algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.1.1 Forward reduction step . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.1.2 Backsubstitution step . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.2 Solving linear systems using Matlab . . . . . . . . . . . . . . . . . . . . . . 32

6 Functions 35

1
6.1 Creating new functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
6.1.1 Gauss elimination using functions . . . . . . . . . . . . . . . . . . . 40
6.1.2 Numerical integration using functions . . . . . . . . . . . . . . . . . 43
6.1.3 Persistent variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.1.4 Global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

7 Vectorizing algorithms 46
7.1 Creating vectors and vector computations . . . . . . . . . . . . . . . . . . 47
7.1.1 Linear algebra vs. component-by-component computations . . . . . 48
7.2 Vectorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.2.1 Vectorizing Gauss elimination . . . . . . . . . . . . . . . . . . . . . 50
7.2.2 Vectorizing numerical integration . . . . . . . . . . . . . . . . . . . 53

1 Numerical integration
Let’s try to develop a program that can be used to integrate any function numerically.
Recall that integration is imply finding the area under a function, as depicted in Figure 1.
A simple method to approximate the integral is by adding the areas of rectangles that
approximate the function f (x) at specific locations between a and b. I can think of at
least 3 methods:

1. A left-point method, where the interval [a, b] is divided in n equal intervals of width
∆x and the height of each rectangle is computed at the left point of each interval.
I.e. the first rectangle’s height is f (a), the next rectangles height is f (a + ∆x), and
so on. The last rectangle’s height is given by f (b − ∆x). This method is depicted
in Figure 1.

f (x)

a ∆x b
Rb
Figure 1: Numerical integration of a
f (x)dx

2
2. A right point method where the rectangle heights are computed as f (a + ∆x), f (a +
2∆x), . . . , f (b).
3. A midpoint method where the rectangle heights are computed as f (a + 21 ∆x), f (a +
3
2
∆x), . . . , f (b − 12 ∆x).

Instead of approximating the integral with rectangles, the area can also be approximated
by trapeziums. The area of a trapezium is given by the product of the average of the two
parallel sides with the perpendicular height. For the trapezium between a and a + ∆x,
the area is
Area = ∆x 12 (f (a) + f (a + ∆x), (1)
as depicted in Figure 2. Should we approximate the total area using trapeziums, the
integral is given as
Z b
f (x)dx ≈ 12 ∆x (f (a) + f (a + ∆x)) + 12 ∆x (f (a + ∆x) + f (a + 2∆a)) +
a
· · · + 12 ∆x (f (b − ∆x + f (b)) (2)
1
= ∆x 2
f (a) + f (a + ∆x) + f (a + 2∆x) + . . .

+f (b − 2∆x) + f (b − ∆x) + 21 f (b) (3)

f (x)

f(a) f(a+∆ x)

a ∆x b ∆x

Figure 2: Numerical integration using the trapezium method.

Now that we have a few ideas of how to integrate a function numerically, let’s develop
the program. First of all, I’ll list the information that has to be available to perform
numerical integration:

1. The function f (x)


2. The bounds of integration
3. The number of intervals
4. The method i.e. left-point, right-point, mid-point or trapezium method

After we get the above information from the user (using a number of input statements),
I’ll compute the interval size ∆x and then use a for loop to add all the areas. Here’s my
implementation of a numerical integration program:

3
1: % Get user inputs
2: F_string = input(’Please enter function to be integrated. ’,’s’);
3: Fcn = inline(F_string);
4: LB = input(’Enter the lower bound of integration. ’);
5: UB = input(’Enter the upper bound of integration. ’);
6: N = input(’How many equal intervals between LB and UB? ’);
7: disp(’1: Left-point 2: Right-point 3: Mid-point’);
8: disp(’4: Trapezium 5: Quit program’);
9: Method = input(’Please enter your option. ’);
10: % My program repeats until the user inputs option 5
11: while Method~=5
12: % Initialize the integral at zero
13: Int = 0;
14: % Compute the interval size Delta_x
15: Delta_x = (UB-LB)/N;
16: % Use the switch statement to branch to one of the 4 methods
17: switch Method
18: % Case 1: left-point method
19: case 1
20: % For loop to compute area of each rectangle and add it to Int
21: for i=1:N
22: x = LB + (i-1)*Delta_x;
23: Int = Int + Delta_x*Fcn(x);
24: end
25: % Case 2: right-point method
26: case 2
27: for i=1:N
28: x = LB + i*Delta_x;
29: Int = Int + Delta_x*Fcn(x);
30: end
31: % Case 3: mid-point method
32: case 3
33: for i=1:N
34: x = LB + (i-1)*Delta_x + 0.5*Delta_x;
35: Int = Int + Delta_x*Fcn(x);
36: end
37: % Case 4: trapezium method
38: case 4
39: for i=1:N
40: x_L = LB + (i-1)*Delta_x;
41: x_R = x_L + Delta_x;
42: Int = Int + 0.5*Delta_x*(Fcn(x_L) + Fcn(x_R));
43: end
44: end
45: % Display answer
46: disp([’Integrating ’,F_string,’ between ’,num2str(LB),’ and ’, ...
47: num2str(UB),’ = ’,num2str(Int)])

4
48: % Display options again
49: disp(’1: Left-point 2: Right-point 3: Mid-point’);
50: disp(’4: Trapezium 5: Quit program’);
51: Method = input(’Please enter your option. ’);
52: end; %End of the while loop
53: % Outside while loop, user entered option 5 = Quit
54: % Displays a friendly message.
55: disp(’Thanks for using the program. Have a good day.’)

Do you notice the use of an outer while loop that keeps on repeating the numerical
integration program as long as the variable Method is not equal to 5. So now I don’t have
to re-type all the inputs when I want to run my program again using a different method.
The program listed above is used to numerically integrate the sin function between 0 and
π
2
. The analytical answer is 1, so let’s see how the program performs using 20 intervals.
I ran the program once and entered option 1 to 5 in that order. The output follows:

Please enter function to be integrated. sin(x)


Enter the lower bound of integration. 0
Enter the upper bound of integration. pi/2
How many equal intervals between LB and UB? 20
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 1
Integrating sin(x) between 0 and 1.5708 = 0.96022
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 2
Integrating sin(x) between 0 and 1.5708 = 1.0388
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 3
Integrating sin(x) between 0 and 1.5708 = 1.0003
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 4
Integrating sin(x) between 0 and 1.5708 = 0.99949
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 5
Thanks for using the program. Have a good day.

1.1 Numerical integration using a while loop

As you can see from the example above 20 intervals is not very accurate for using either the
left-point or right-point methods, but it seems adequate for the mid-point and trapezium

5
methods. So how do we know in advance how many intervals we need to get an accurate
numerically integrated value? We don’t, so therefore I suggest the use of a while loop to
keep increasing the number of intervals until the numerically integrated result converges
to within some specified tolerance. Here follows the modified program:

1: % Get user inputs


2: F_string = input(’Please enter function to be integrated. ’,’s’);
3: Fcn = inline(F_string);
4: LB = input(’Enter the lower bound of integration. ’);
5: UB = input(’Enter the upper bound of integration. ’);
6: N_ini = input(’Initial number of intervals between LB and UB? ’);
7: acc = input(’Enter the required accuracy. ’);
8: disp(’1: Left-point 2: Right-point 3: Mid-point’);
9: disp(’4: Trapezium 5: Quit program’);
10: Method = input(’Please enter your option. ’);
11: % My program repeats until the user inputs option 5
12: while Method~=5
13: % Define initial Error greater as acc, to enter while loop
14: Error = 2*acc;
15: % Since the number of intervals change during program execution,
16: % reset N to the initial number of intervals
17: N = N_ini;
18: % Set Int to zero to have a value the first time
19: % the while loop is executed
20: Int = 0;
21: % Start while loop that checks if consecutive values converged
22: while Error>acc
23: % Make a copy of the previous integral just before the program
24: % section starts that computes the new value
25: Int_old = Int;
26: % Initialize the integral at zero
27: Int = 0;
28: % Compute the interval size Delta_x
29: Delta_x = (UB-LB)/N;
30: % Use the switch statement to branch to one of the 4 methods
31: switch Method
32: % Case 1: left-point method
33: case 1
34: % For loop to compute area of each rectangle and add it to Int
35: for i=1:N
36: x = LB + (i-1)*Delta_x;
37: Int = Int + Delta_x*Fcn(x);
38: end
39: % Case 2: right-point method
40: case 2
41: for i=1:N
42: x = LB + i*Delta_x;

6
43: Int = Int + Delta_x*Fcn(x);
44: end
45: % Case 3: mid-point method
46: case 3
47: for i=1:N
48: x = LB + (i-1)*Delta_x + 0.5*Delta_x;
49: Int = Int + Delta_x*Fcn(x);
50: end
51: % Case 4: trapezium method
52: case 4
53: for i=1:N
54: x_L = LB + (i-1)*Delta_x;
55: x_R = x_L + Delta_x;
56: Int = Int + 0.5*Delta_x*(Fcn(x_L) + Fcn(x_R));
57: end
58: end; % End of switch
59: % Compute Error i.e. difference between consecutive integrals
60: Error = abs(Int - Int_old);
61: % Double the number of intervals
62: N = 2*N;
63: end; % End of inner while
64: % Display answer
65: disp([’Integrating ’,F_string,’ between ’,num2str(LB),’ and ’, ...
66: num2str(UB),’ = ’,num2str(Int)])
67: % Also display the number of required intervals
68: disp([’Number of intervals required = ’,num2str(N/2)])
69: % Display options again
70: disp(’1: Left-point 2: Right-point 3: Mid-point’);
71: disp(’4: Trapezium 5: Quit program’);
72: Method = input(’Please enter your option. ’);
73: end; %End of outer while loop
74: % Outside while loop, user entered option 5 = Quit
75: % Displays a friendly message.
76: disp(’Thanks for using the program. Have a good day.’)

I’ll only discauss the changes I’ve made. In program line 6, I now call the number of
intervals N ini, to indicate that this is the initial number of intervals. As the program
proceeds, the number of intervals increase. In program line 7 I also ask the user to enter
the required accuracy, which will be used in the while loop condition.
In program line 14 I define a variable Error, which is the difference between consecutive
numerical integrals (using a different number of intervals). The number of intervals is
initially set to the value specified by the user in program line 17.
In line 20 I set the variable Int to zero. This statement is required because I assign the
value of Int to the variable Int old in program line 25. The very first time the while
loop in line 22 is executed no calculations have been performed and therefore no value is
available in the variable Int. Hence I require line 20.

7
Program lines 27–58 compute the numerical integral, based on the chosen method. This
part of the program is unchanged. In line 60 I compute the difference between the integral
and the value obtained previously. I then double the number of intervals in line 62 and
the end statement in line 63 returns the program to the while statement in line 22. If
the difference between consecutive numerical integration values (computed in line 60) is
smaller than acc the while loop terminates and the program continues at program line
65. Answers are displayed in the Command Window and the options are re-displayed.
Depending on the user’s input, the program is either re-run (Method = 1–4), or terminates
with a friendly message (Method = 5). If Error>acc, the while loop (program lines 25–
62) is repeated again.
Here follows the output for the same example I presented earlier. An accuracy of 0.0001
is specified. As you can see, the different methods require a different number of intervals
to achive this specified accuracy. Clearly the mid-point and trapezium methods (N=80)
is much more efficient that the left-point and right-point methods (N=10240).

Please enter function to be integrated. sin(x)


Enter the lower bound of integration. 0
Enter the upper bound of integration. pi/2
How many equal intervals between LB and UB? 10
Enter the required accuracy. 0.0001
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 1
Integrating sin(x) between 0 and 1.5708 = 0.99992
Number of intervals required = 10240
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 2
Integrating sin(x) between 0 and 1.5708 = 1.0001
Number of intervals required = 10240
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 3
Integrating sin(x) between 0 and 1.5708 = 1
Number of intervals required = 80
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 4
Integrating sin(x) between 0 and 1.5708 = 0.99997
Number of intervals required = 80
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 5
Thanks for using the program. Have a good day.

8
2 Vectors and matrices
So far, we have only dealt with variables that contain a single value i.e. scalar variables.
But how do we represent a quantity that contains more than a single value e.g. a vector?
We can use scalar variables to represent each of the components of the vector, but that
will require many variable names, especially if the vector has many components. Fortu-
nately, there exists array variables (computer terminology for both vectors and matrices)
in Matlab.
I’ll start of by showing an example of a vector in Matlab. A vector is defined by enclosing
the components in square brackets e.g.

a = [ 1
2
3 ];

defines a column vector a with three components and

b = [ 1 2 3 ];

defines a row vector b with the same three components. Different rows can also be
indicated with semi-colons e.g.

a = [1; 2; 3];

also defines a column-vector a.


A matrix variable is also defined by enclosing the components in square brackets e.g.

A = [ 1 2 3
2 3 4
3 4 5 ];

defines a 3 × 3 symmetric matrix A. As before, different rows can be indicated with a


semi-colon i.e.

A = [ 1 2 3; 2 3 4; 3 4 5];

defines the same 3 × 3 matrix A as above.


You do not need to define complete vectors and matrices as above. You can also define
vectors and matrices component by component by using an index to specify the row and
column. As an example, the following three program lines defines three components of a
vector b:

b(1) = 1;
b(2) = 2;
b(3) = 3;

9
The above three program lines defines the same vector b as at the start of this section.
Note that if we specify only a single index such as in the example above, Matlab defines
a row vector. If we want to create a column vector, we have to explicitly state that we
are assigning values to the first column of an array e.g.

a(1,1) = 1;
a(2,1) = 2;
a(3,1) = 3;

defines the same vector a as at the start of this section.


Note that if we do not assign all the components of a vecor, Matlab fills in the gaps with
zeros. As an example, the two program lines

a(1) = 1;
a(4) = 4;

define a row vector a

a =
1 0 0 4

Matrices can also be defined component by component by specifying two indices (the first
index refers to the row number and the second index refers to the column number). As
an example, the matrix A above is defined by:

A(1,1) = 1;
A(1,2) = 2;
A(1,3) = 3;
A(2,1) = 2;
A(2,2) = 3;
A(2,3) = 4;
A(3,1) = 3;
A(3,2) = 4;
A(3,3) = 5;

Note that if we define a matrix component by component, Matlab will fill in any non-
specified entries with zeros. As an example, the three program lines

A(1,1) = 7;
A(1,3) = 2;
A(2,2) = 9;

define a 2 × 3 matrix

A =
7 0 2
0 9 0

As before, the components that are not explicitly defined are set to zero.

10
2.1 Defining a matrix using the input statement

A student asked me wether or not a matrix can be read in using the input statement. As
we’ve seen before, the input statement is very convenient to read a single variable from
the Command Window. But how can we read all the entries of a matrix using the input
statement? I can think of a simple program making use of two for loops:

1: % Ask the user how many rows the matrix has


2: Rows = input(’How many rows in the matrix? ’);
3: % Ask the user how many columns the matrix has
4: Columns = input(’How many columns in the matrix? ’);
5: % Initialize the matrix to all zeros
6: Matrix = zeros(Rows,Columns);
7: % For loop over the rows
8: for i = 1:Rows
9: % For loop over the columns
10: for j = 1:Columns
11: % Use input statement to read the i,j component
12: Matrix(i,j) = input([’Enter component (’,num2str(i),’,’, ...
13: num2str(j),’) please. ’]);
14: end
15: end

You’re supposed to be familiar with all the programming ideas I’ve used here. One exep-
tion is the three dots at the end of program line 12. This is how to indicate continuation
in Matlab i.e. any line that ends with three dots continues on the next line. This is very
convenient if you are programming a complicated expression that doesn’t fit in one line
of the editing window. Whenever you use the continuation dots, make sure that you do
it at an appropriate place in the program. I usually try to use it either before or after
any mathematical operation. DO NOT use it in the middle of a variable name e.g. the
following statement is NOT VALID

3: Value1 = Very_long_variable_name1 + Very_long_variable_name2 - Very ...


_long_variable_name3;

2.2 Matrix manipulations

Other than refer to a single matrix entry at a time, many other matrix manipulations are
possible in Matlab. I’ll illustrate the possibilities by using the 4 × 5 matrix

A =
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20

11
1. C = A(2,3): this refers to the matrix component in row 2, column 3 i.e. C = 8.
2. C = A([1 4],[1 5]): this refers to the entries in matrix A in rows 1 and 4, and
columns 1 and 5 i.e.

C =
1 5
16 20

3. C = A([2 4],[2 3 5]): refers to the entries in rows 2 and 4, and columns 2, 3 and
5 i.e.

C =
7 8 10
17 18 20

4. C = A([1 1 1 1],[3 4]): refers to the entries in row 1 (repeated 4 times) and
columns 3 and 4 i.e.

C =
3 4
3 4
3 4
3 4

5. C = A([4 3 2 1],[5 4 3 2 1]): refers to the entries in rows 4, 3, 2 and 1 (in that
order) and columns 5, 4, 3, 2 and 1 (in that order) i.e.

C =
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1

6. C = A(1:2:end,1:2:end): This refers to all the rows starting at 1 with increments


of 2 to the end,and all the columns starting at 1 with increments of 2 to the end.
Since A has 4 rows and 5 columns, this means rows 1 and 3, ansd columns 1, 3 and
5 i.e.

C =
1 3 5
11 13 15

7. Matlab contains many built-in matrix manipulation commands. Examples are




B=flipud(A): The rows of the matrix A are sorted in reversed order (the matrix
is flipped in the up-down direction).


B=fliplr(A): The columns of the matrix A are sorted in reversed order (the
matrix is flipped in the left-right direction).


B=A’: The rows and columns of matrix A are switched i.e. B is the transpose of
A.

12
2.3 Example: Norm of a vector

To further illustrate the use of vectors, let’s develop a program that computes the norm
(magnitude or length) of a vector a. Let’s assume the vector has three components.

1: % Define the vector a


2: a = [ 1 2 3 ];
3: % Start the value of the norm at zero
4: Norm = 0.0;
5: for i = 1:3
6: % Add the square of each component to the norm
7: Norm = Norm + a(i)^2;
8: end
9: % Take the square root of the sum of squares
10: Norm = sqrt(Norm);
11: % Display the result
12: disp([’Norm of vector = ’,num2str(Norm)]);

The above program will only work for vectors with three components. Let’s rather change
the program to work for vectors of any length. We need the length statement to accom-
plish this goal. The syntax of the length statement is

N = length(a);

which will assign the number of components of the vector a to the variable N. A program
that can compute the norm of a vector of any length follows:

1: % Define the vector a, in this case with 8 components


2: a = [ 2 4 3 1 2 3 7 6 ];
3: % Start the value of the norm at zero
4: Norm = 0.0;
5: % Get the number of components of a
6: N = length(a);
7: for i = 1:N
8: % Add the square of each component to the norm
9: Norm = Norm + a(i)^2;
10: end
11: % Take the square root of the sum of squares
12: Norm = sqrt(Norm);
13: % Display the result
14: disp([’Norm of vector = ’,num2str(Norm)]);

Program lines 1–2 defines the vector a (which can be anything, this particular example
contains 8 components). The norm of any vector can be computed by just changing
program line 2.

13
2.4 Example: Matrix-vector multiplication

Let’s develop a program that can multiply a matrix with a vector. Recall from your linear
algebra course that matrix-vector multiplication is defined as
n
X
ci = Aij aj for i = 1, 2, . . . , m (4)
j=1

where the m × n matrix A is multiplied with the n × 1 vector a to give the m × 1 vector
c. I’ll also illustrate Eq.(4) with a numerical example. Let’s multiply a 2 × 3 matrix A
with a 3 × 1 vector a to obtain a 2 × 1 vector c:
 
  1     
3 2 5 3×1+2×2+5×3 22
2 = = (5)
2 3 1   2×1+3×2+1×3 11
3

Now let’s try to write a program that can multiply a m × n matrix A with a n × 1 vector.
First of all, you must recognize that we will use for loops for our computations. This is
because before we start multiplying a matrix with a vector, the matrix and vector must be
known. So we will know how many rows and columns the matrix has. Since the number
of rows and columns are known, we will use for loops.
We also need to know how many for loops are required. We take guidance directly
from Eq.(4). We see that two distinct counters are required, namely i = 1, 2, . . . , m
and j = 1, 2, . . . , n. The i counter refers to the current component of the c vector I’m
computing, while the j counter is used to sum the appropriate products.
The order in which these two for loops must appear is not unique, as long as the correct
logic is used. However, I prefer to use the for loop over the i counter as the outer loop,
within which I use the for loop over the j counter to perform the addition of terms. The
reason for this choice is that my program now reflects the same method I use when I
multiply a matrix with a vector using pen and paper. I first decide which component I
want to compute (a specific value for i), then I compute this component by adding the
appropriate products (using the second for loop with j = 1, 2, . . . , n). If I use the j
counter in the outer loop and the i counter in the inner loop, I’m computing the first
contribution to all the components of c, then adding the second contribution to all the
components of c and so on. I find such a program confusing, because this does not reflect
the usual method of multiplying a matrix and vector.
One last statement we need before we can write the program: the size statement is used
to get the number of rows and columns of a matrix. The usage is

[rows,columns]=size(Matrix);

where the function size assigns the number of rows and columns of the matrix Matrix
to the variables rows and columns. Now we are ready to write a program that multiplies
a matrix with a vector:

1: % Create a random matrix A

14
2: A = rand(10,8);
3: % Create a random vector a
4: a = rand(8,1);
5: % Get the size of the matrix A
6: [Rows,Columns] = size(A);
7: % For loop over the number of rows
8: for i=1:Rows
9: % Initialize the i-th entry of the c-vector with zero
10: c(i,1) = 0.0;
11: % For loop over the number of columns
12: for j=1:Columns
13: % Add the appropriate term to the c-vector
14: c(i,1) = c(i,1) + A(i,j)*a(j);
15: end
16: end
17: % Displays output
18: disp(’The product of matrix A with vector a is’)
19: disp(c)

Program lines 1–4 creates a random matrix A and a random vector a, just to test our
program. Program lines 5–16 contain the actual matrix-vector multiplication and program
lines 17–19 create the output to the Command Window.

2.5 Example: Plots of functions of a single variable

We can use vectors to generate plots of any function of one variable. First of all, you’ll need
to generate a vector of all the x-values where you want the function to be evaluated. Then
you compute the associated y-value at each of these x-values. A plot is then generated
by simply typing plot(x,y). Here’s an example to plot the function

y = sin(x) for x ∈ [−π; π] (6)

1: % I’ve decided I want 100 intervals between -pi and pi to


2: % plot the graph so I use a for loop that repeats 101 times
3: for i = 0:100;
4: % I compute the x component as x(1) = -pi for i=0
5: % all the way to x(101) = pi for i=100
6: x(i+1) = -pi + 0.01*i*2*pi;
7: % The y component is computed for every x component
8: y(i+1) = sin(x(i+1));
9: end
10: % Plot the vectors x vs. y
11: plot(x,y)

The program produces the image in Figure 3. Experiment with various other functions
and see if you can generate the associated graphs.

15
Figure 3: Plot of y = sin(x) generated by Matlab.

3 Revisiting previous programs


I’ll give a few more examples of the use of vectors. I’ll first use vectors to compute a Taylor
series approximation. Then I’ll illustrate how vectors can be used to perform numerical
integration.

3.1 Taylor series using vectors

I discussed Taylor series analyses in part 1 of the lecture notes. I used both for loops and
while loop to perform the required computations. Revisit the notes before you continue.
You should have noticed that when we computed a Taylor series approximation at the
start of the year, we only used scalar variables. We kept on adding new terms in the series
until the series converged.
Let’s compute a Taylor series approximation again, but now making use of two vectors.
In the first vector, called Terms, I will save the Taylor series terms i.e. the first term in the
series is saved in the first component, the second term in the series is saved in the second
component, and so on. In the second vector, called Taylor approx, I will save the current
value of the Taylor series approximation. The first component will contain the first term,
the second component will contain the sum of the first two terms, and so on. We proceed
like this and save the n term Taylor series approximation in the n-th component of the
vector Taylor approx. When the complete vectors are displayed, we should clearly see

16
how the components of the vector Terms become smaller, while the components of the
vector Taylor approx should converge to the analytical result.
As an example, lets compute the Taylor series approximation to the exponential function,
which is given by

x 1 2 1 3
X xk
e = 1 + x + 2x + 6x + · · · = (7)
k=0
k!

Here’s a program that uses a while loop to compute the Taylor series approximation to
ex within a specified accuracy.

1: % Get value for x at which to compute exp(x)


2: x = input(’What value for x do you want to compute exp(x) at? ’);
3: % Get required accuracy
4: acc = input(’How accurate must exp(x) be computed? ’);
5: % 1-term Taylor series approximation = x^0 = 1
6: Terms(1) = 1;
7: Taylor_approx(1) = 1;
8: % Set term counter to 1
9: k = 1;
10: % Set initial Error>acc to make sure while loop is entered
11: Error = 2*acc;
12: while Error>acc
13: % Compute next term in series
14: Terms(k+1) = x^k/prod(1:k);
15: % Next component in vector equal to previous component plus new term
16: Taylor_approx(k+1) = Taylor_approx(k) + Terms(k+1);
17: % Error = difference between consecutive components
18: Error = abs(Taylor_approx(k+1)-Taylor_approx(k));
19: % Increment the counter k
20: k = k + 1;
21: end
22: % Displays the vectors Terms Series in Command Window
23: Terms
24: Taylor_approx

Here’s the output of the program listed above, using format long. You can clearly see
how the Taylor series converges to e = 2.718281828459 . . . as the number of terms in the
series increases.

What value for x do you want to compute exp(x) at? 1


How accurate must exp(x) be computed? 1e-7
Terms =
Columns 1 through 4
1.00000000000000 1.00000000000000 0.50000000000000 0.16666666666667
Columns 5 through 8
0.04166666666667 0.00833333333333 0.00138888888889 0.00019841269841

17
Columns 9 through 12
0.00002480158730 0.00000275573192 0.00000027557319 0.00000002505211
Taylor_approx =
Columns 1 through 4
1.00000000000000 2.00000000000000 2.50000000000000 2.66666666666667
Columns 5 through 8
2.70833333333333 2.71666666666667 2.71805555555556 2.71825396825397
Columns 9 through 12
2.71827876984127 2.71828152557319 2.71828180114638 2.71828182619849

Modify the above program to compute the Taylor series approximation to π. Be careful
not to specify a very small accuracy. You’ll see that the π Taylor series converges very
slowly, so you need a large number of terms for an accurate approximation.

3.2 Numerical integration using vectors

Let’s revisit the numerical integration program developed in Section 1. In that section,
the variable Int old is used to have the previous value of the numerical integral available
after the new value is computed. Now I propose that we not only save the most recent
value of the numerical integral, but that we save the numerical integral for every case
analyzed. We can save all these values in a single vector.
I’m going to create two vectors. In the first vector, called N, I’ll save the number of
intervals. As the number of intervals change, I’ll save the new number of intervals in the
next component of the vector N. The second vector, called Int, will contain the numerical
integrals associated with the number of intervals in the vector N.
Here’s the modified numerical integration program:

1: % Get user inputs


2: F_string = input(’Please enter function to be integrated. ’,’s’);
3: Fcn = inline(F_string);
4: LB = input(’Enter the lower bound of integration. ’);
5: UB = input(’Enter the upper bound of integration. ’);
6: N_ini = input(’Initial number of intervals between LB and UB? ’);
7: acc = input(’Enter the required accuracy. ’);
8: disp(’1: Left-point 2: Right-point 3: Mid-point’);
9: disp(’4: Trapezium 5: Quit program’);
10: Method = input(’Please enter your option. ’);
11: % My program repeats until the user inputs option 5
12: while Method~=5
13: % Define initial Error greater as acc, to enter while loop
14: Error = 2*acc;
15: % Number of intervals is saved in a vector N. I start with component 2
16: % to correspond to the integrals in the vector Int
17: N(2) = N_ini;
18: % Set counter k to one

18
19: k = 1;
20: % Set component 1 of the Int vector to zero.
21: Int(1) = 0;
22: % Start while loop that checks if consecutive values converged
23: while Error>acc
24: % Increment the counter k with one
25: k = k + 1;
26: % Initialize the current component of the Int vector to zero
27: Int(k) = 0;
28: % Compute the interval size Delta_x
29: Delta_x = (UB-LB)/N(k);
30: % Use the switch statement to branch to one of the 4 methods
31: switch Method
32: % Case 1: left-point method
33: case 1
34: % For loop to compute area of each rectangle and add it to Int
35: for i=1:N(k)
36: x = LB + (i-1)*Delta_x;
37: Int(k) = Int(k) + Delta_x*Fcn(x);
38: end
39: % Case 2: right-point method
40: case 2
41: for i=1:N(k)
42: x = LB + i*Delta_x;
43: Int(k) = Int(k) + Delta_x*Fcn(x);
44: end
45: % Case 3: mid-point method
46: case 3
47: for i=1:N(k)
48: x = LB + (i-1)*Delta_x + 0.5*Delta_x;
49: Int(k) = Int(k) + Delta_x*Fcn(x);
50: end
51: % Case 4: trapezium method
52: case 4
53: for i=1:N(k)
54: x_L = LB + (i-1)*Delta_x;
55: x_R = x_L + Delta_x;
56: Int(k) = Int(k) + 0.5*Delta_x*(Fcn(x_L) + Fcn(x_R));
57: end
58: end; % End of switch
59: % Compute Error i.e. difference between consecutive integrals
60: Error = abs(Int(k) - Int(k-1));
61: % Component k+1 of N vector = 2 times component k value
62: N(k+1) = 2*N(k);
63: end; % End of inner while
64: % Display answer
65: disp([’Integrating ’,F_string,’ between ’,num2str(LB),’ and ’, ...

19
66: num2str(UB)])
67: disp(’Intervals Integral’);
68: % Display first k components of vectors N and Int
69: disp([num2str(N(1:k)’,’%8d\t\t’),num2str(Int(1:k)’,’%10.6f’)])
70: % Display options again
71: disp(’1: Left-point 2: Right-point 3: Mid-point’);
72: disp(’4: Trapezium 5: Quit program’);
73: Method = input(’Please enter your option. ’);
74: end; %End of outer while loop
75: % Outside while loop, user entered option 5 = Quit
76: % Displays a friendly message.
77: disp(’Thanks for using the program. Have a good day.’)

Very few changes were made compared to the previous version. I define a counter, k in
this program, which keep track of the number of components in the vectors N and Int.
k is initialized in program line 19 and is incremented with 1 inside the while loop in
program line 25.
Since the number of intervals N is now a vector, I need to specify which component of N
I need e.g. program lines 17, 29 and 35. Similarly the numerical integrals are now stored
in the vector Int, so I need to refer to a specific component of Int, e.g. program lines 27,
37 and 43.
I’ve also decided to change the output. Now I display the first k components of the vectors
N and Int. Program line 69 displays these vectors. I make use of the num2str command
to change the numerical values to strings. The number of intervals N is converted to a
string by using

num2str(N(1:k)’,’%8d\t\t’)

which converts the first k components of N to a string. The d in the statement above
refers to an integer conversion and the \t indicates a tab. So every component of N is
converted to an string and two tabs are added. The numerical integrals in the vector Int
are converted to strings with the statement

num2str(Int(1:k)’,’%10.6f’)])

where the f in the statement above refers to a real number conversion, making use of a
maximum of 10 digits of which 6 is used for the digits after the decimal point. In the
lecture notes part 1 I also comment on the use of the formatted num2str command.
Here follows the output from the program. I only executed option 2 (right-point) and
option 4 (trapezium) before I quit the program (option 5). Note the tabular output, where
we now clearly see the progress towards the analytical result as the number of intervals
is increased.

Please enter function to be integrated. sin(x)


Enter the lower bound of integration. 0

20
Enter the upper bound of integration. pi/2
Initial number of intervals between LB and UB? 10
Enter the required accuracy. 0.0001
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 2
Integrating sin(x) between 0 and 1.5708
Intervals Integral
0 0.000000
10 1.076483
20 1.038756
40 1.019506
80 1.009785
160 1.004901
320 1.002452
640 1.001227
1280 1.000613
2560 1.000307
5120 1.000153
10240 1.000077
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 4
Integrating sin(x) between 0 and 1.5708
Intervals Integral
0 0.000000
10 0.997943
20 0.999486
40 0.999871
80 0.999968
1: Left-point 2: Right-point 3: Mid-point
4: Trapezium 5: Quit program
Please enter your option. 5
Thanks for using the program. Have a good day.

4 Sorting algorithms
Let’s further develop our programming skills by trying to write a program that can sort
the components of a vector in ascending order (small to large). In order to develop a
sorting algorithm, you have to think of a simple operation that a computer can perform
that will enable a vector te be sorted. If required, this operation can be repeated a large
number of times (which will be the case).
The operation I’m referring to is to compare two numbers. If the number pair is in the
correct order (1st value smaller than the 2nd), then move to a different number pair. If
the number pair is not in the correct order, swap the 2 numbers. This simple operation

21
is sufficient to order a vector.
I can think of a few sorting algorithms. I’ll start off with a method that is easy to
explain and implement, but not very efficient. Let’s compare the first component of the
vector with all the other components, one by one (starting at component 2, ending at the
last component), i.e. compare component 1 with 2, then component 1 with 3 and so on.
Whenever the 1st component is larger than the component it is compared to, we swap the
components. If not, we simply move to the next component of the vector. When we reach
the end of the vector, the smallest number will be in component 1. We start at the top
again but now we compare the 2nd component of the vector with all the components from
3 to the end (It’s no use to start at 1 again, it already contains the smallest number). We
repeat this process till we compare the last two numbers. This process is implemented
below. We need two nested for loops: the first loop provides the index of the 1st number
in the number pair, while the second loop provides the index of the 2nd number in the
number pair.

1: % Create the vector a


2: a = [5 4 3 2 1];
3: % Get how many components the vector has
4: n = length(a);
5: % First for loop: position of first number in pair
6: for i=1:n-1
7: % Second for loop: position of second number in pair
8: for j=i+1:n
9: % Check if number pair is in ascending order
10: if a(i)>a(j)
11: % Swap numbers if they are in the wrong order
12: a_copy = a(i);
13: a(i) = a(j);
14: a(j) = a_copy;
15: end
16: end
17: % Display the vector each time the outer for loop is complete
18: a
19: end

In program line 2 I define the vector a. I’ve decided to use a vector that is sorted from
large to small. Such a vector will really test if my program can sort in ascending order.
Program line 4 gets the number of components of the vector. The first for loop is coded
in program line 6: do you see that the index of the first number in my number pair starts
at 1 and ends at n-1, one from the end. The second for loop is coded in program line
8. The 1st number in the number pair is given by index i. I now have to compare this
number with the remaining numbers below position i in the vector. That is why the
second index j starts at i+1 (the very next component in the vector) and ends at n, the
last entry of the vector.
In program line 10 I check if the number pair is in the correct order. If the i-th component
(1st number of the pair) is greater than the j-th component, I swap the pair of numbers.

22
This is done by first making a copy of the i-th component (program line 12), then assigning
the old j-th component to the new i-th component (program line 13). Finally the copy
of the old i-th component is assigned to the new j-th component (program line 14).
Program line 18 simply displays the vector every time the outer for loop is complete.
This will provide output that will illustrate how the program proceeds to sort the vector.
Here follows the output of the above program:

a =
1 5 4 3 2
a =
1 2 5 4 3
a =
1 2 3 5 4
a =
1 2 3 4 5

As you can see, the program correctly sorts the random vector into acsending order. After
the for loop is executed once, the smallest number occupies the first vector position. After
the for loop is executed again, the first 2 numbers are in their correct positions. This
process repeats until all the numbers are correct after the for loop has been executed 4
times.

4.1 Bubble sort algorithm

Let’s also look at an alternative sorting algorithm. It will help to illustrate that many
solutions exist to the same problem. The bubble sorting algorithm also checks if a number
pair is in the correct order. The only difference with the algorithm above is that the bubble
sort algorithm always compares a component with the very next component in the vector,
i.e. compare component 1 with 2, then component 2 with 3, then component 3 with 4
and so on. After the last number pair has been compared, the largest number will occupy
the last position in the vector. Now we have to start at the top again and compare
component 1 with 2, 2 with 3 and so on. This time we stop 1 pair from the bottom,
because we already know the largest number is in the correct position. This process is
repeated till only components 1 and 2 are compared. Here’s my implementation of the
bubble sort algorithm:

1: % Create the vector a


2: a = [5 4 3 2 1];
3: % Get how many components the vector has
4: n = length(a);
5: % First for loop: how many times the vector
6: % has to be scanned from top to bottom
7: for i=1:n-1;
8: % The index to the first number of the pair
9: for j=1:n-i

23
10: % Compare the j-th and j+1-th vector components
11: if(a(j)>a(j+1))
12: % Swap component j and j+1 if in wrong order
13: a_copy = a(j);
14: a(j) = a(j+1);
15: a(j+1) = a_copy;
16: end
17: end
18: % Display the vector a every time the for loop is executed
19: a
20: end

The output of the bubble sort algorithm, using the same 5 component vector as before,
follows:

a =
4 3 2 1 5
a =
3 2 1 4 5
a =
2 1 3 4 5
a =
1 2 3 4 5

As you can see the largest entry occupies the last vector position after the outer for loop
is executed once. The largest two entries are in their correct positions after the outer for
loop is executed again. This process repeats until all entries are correct after outer the
for loop is executed four times.
I’ll now run the bubble sort program again, but this time I replace program line 2 with

2: a = rand(1,8);

which will create a random 8 component vector a. I include the output of the bubble sort
algorithm for one of the cases I ran after the above change:

a =
0.0153 0.4451 0.7468 0.4660 0.4186 0.8462 0.5252 0.9318

a =
0.0153 0.4451 0.4660 0.4186 0.7468 0.5252 0.8462 0.9318

a =
0.0153 0.4451 0.4186 0.4660 0.5252 0.7468 0.8462 0.9318

a =
0.0153 0.4186 0.4451 0.4660 0.5252 0.7468 0.8462 0.9318

24
a =
0.0153 0.4186 0.4451 0.4660 0.5252 0.7468 0.8462 0.9318

a =
0.0153 0.4186 0.4451 0.4660 0.5252 0.7468 0.8462 0.9318

a =
0.0153 0.4186 0.4451 0.4660 0.5252 0.7468 0.8462 0.9318

The algorithm clearly works. For the 8 component vector above, the outer for loop is
executed 7 times. However, the output for this particular example shows that the vector
is in the correct order after only 4 outer for loop executions. So how can we improve the
bubble sort algorithm to stop once the vector is in the correct order?
The answer is quite simple. We have to keep track of number pair swapping. If we find
that we start at the top of the vector and scan all the way to the bottom and never find
a number pair in the wrong order, we know the vector is sorted and the algorithm can
stop. This type of logic requires a while loop.

1: % Create the vector a


2: a = rand(1,8);
3: % Get how many components the vector has
4: n = length(a);
5: % Start index at zero
6: i = 0;
7: % Set flag to 0 i.e. assume vector is not ordered
8: flag = 0;
9: % Start while loop: repeat loop while index i<n-1
10: % or the flag not equal to 0
11: while (flag == 0) & (i < n-1);
12: % Set flag to 1 i.e. assume vector is ordered
13: flag = 1;
14: % Increment index with 1
15: i = i + 1;
16: % The index to the first number of the pair
17: for j=1:n-i
18: % Compare the j-th and j+1-th vector components
19: if(a(j)>a(j+1))
20: % Swap component j and j+1 if in wrong order
21: a_copy = a(j);
22: a(j) = a(j+1);
23: a(j+1) = a_copy;
24: % Set flag to 0 to indicate vector is not ordered yet
25: flag = 0;
26: end
27: end

25
28: % Display the vector a every time the for loop is executed
29: a
30: end
31: % Display the number of times the while loop is executed
32: disp([’While loop repeated ’,num2str(i),’ times.’])

I’ll only explain the program lines that are new. The index i is set equal to zero in
program line 6 and is incremented by one inside the while loop in program line 15. This
replaces the for loop that was repeated a predetermined number of times (n-1). I also
use a new variable called flag to indicate whether the vector is sorted or not. I set flag
equal to zero in program line 8, before the while loop starts. The while loop condition
in program line 11 states that the sorting loop is repeated as long as the variable flag
equals zero and the index i is less than n-1 (We know that the vector will be sorted once
i = n-1). As soon as the loop starts, I set the variable flag equal to 1 in program line
13 i.e. I assume that the vector is already in the correct order. Should I find a pair of
numbers that is not in the correct order, I change the value of the variable flag to zero
(program line 25). The rest of the program is unchanged. At the end of the while loop I
display the value of the index i, the number of times the while loop was repated.
Here’s an example of the modified bubble sort algorithm. I chose a case in which the
original vector a was almost sorted already, so the while loop is repeated only 3 times for
a vector of length 8. As you can see the program now terminates as soon as the vector is
scanned from top to bottom without a number pair swapping. In fact, the while loop is
repeated only once for any vector that is already sorted in ascending order.

a =
0.1850 0.1852 0.6375 0.4383 0.6526 0.6605 0.8145 0.8526

a =
0.1850 0.1852 0.4383 0.6375 0.6526 0.6605 0.8145 0.8526

a =
0.1850 0.1852 0.4383 0.6375 0.6526 0.6605 0.8145 0.8526

While loop repeated 3 times.

5 Gauss elimination
The solution of a system of linear equations remains one of the numerical computations
performed most often in all engineering disciplines. In your linear algebra course, you have
already mastered Gauss elimination. Gauss elimination remains one of the most efficient
direct methods to solve large systems of linear equations. Let’s revisit the method.
During the forward reduction step of Gauss elimination, the original system matrix is re-
duced to a system that contains zeros below the diagonal. This is achieved by subtracting

26
a fraction of one row from thenext. I’ll illustrate with a 4 × 4 system:
    
2 3 4 1   x1  
 10 

 1 1 2 1 

x 2
 
5


 2
 = (8)
4 5 2   x3   13 
  
1 2 3 4 x4 10
  

Replace Row 2 of the system with Row 2 - 21 × Row 1 i.e.
    
2 3 4 1 
 x 1 
   10 

 0 −0.5 0 0.5   x2   0 

 2
 = (9)
4 5 2   x3   13 
  
1 2 3 4 x4 10
  

Then replace Row 3 with Row 3 - 22 × Row 1 i.e.
    
2 3 4 1 
 x1  
 10 

 0 −0.5 0 0.5   x2   0 

 0
 = (10)
1 1 1   x3   3 
  
1 2 3 4 x4 10
  

Finally, we replace Row 4 with Row 4 - 21 × Row 1 i.e.
    
2 3 4 1 
 x 1 
   10 

 0 −0.5 0 0.5   x2   0 

 0
 = (11)
1 1 1   x3   3 
  
0 0.5 1 3.5 x4 5
  

We have succeeded in reducing the first column below the diagonal to zeros. Now we
proceed to reduce the second column  below the diagonal to zeros. This is achieved by
1
replacing Row 3 with Row 3 - −0.5 × Row 2 i.e.
    
2 3 4 1 
 x 1 
   10 

 0 −0.5 0 0.5   x2   0 

 0
 = (12)
0 1 2   x3  
 3 
0 0.5 1 3.5 x4 5
   

0.5

The last entry in column 2 is reduced to zero by replacinng Row 4 with Row 4 - −0.5 ×
Row 2 i.e.     
2 3 4 1 
 x1  
 10 

 0 −0.5 0 0.5   x2   0 

 0
 = (13)
0 1 2   x3   3 
  
0 0 1 4 x4 5
  

We complete the forward reduction step by reducing the  column 3 entries below the
diagonal (there is only 1) to zero i.e. Row 4 = Row 4 - 11 × Row 3 i.e.
    
2 3 4 1 
 x 1 
   10 

 0 −0.5 0 0.5   x2   0 

 0
 = (14)
0 1 2   x3  
 3 

0 0 0 2 x4 2
   

27
We are now done with the forward reduction step. All the entries of the matrix below the
diagonal is zero. Now we proceed to solve the unknowns x4 , x3 , x2 and x1 , in that order.
This part of the Gauss elimination process is called backsubstitution.
The 4th equation in the above system reads
2x4 = 2 (15)
which is solved to provide x4 = 1. The 3rd equation reads
x3 + 2x4 = 3 (16)
which is used to solve x3 as
x3 = 3 − 2x4 = 1 (17)
We continue with this process to solve x2 from the 2nd equation:
0 − 0x3 − 0.5x4
x2 = =1 (18)
−0.5
Finally, x1 is solved from the 1st equation:
10 − 3x2 − 4x3 − x4
x1 = =1 (19)
2

5.1 Gauss elimination algorithm

Now that we have reviewed the Gauss elimination process, let’s attempt to program it.
You will find this program quite challenging, and will have to review the steps numerous
times before you finally understand them all.
To perform Gauss elimination, we require a linear system to solve. I’ll use the same linear
system as above during the program development.
First of all, we have to decide what type of computations are required during Gauss
elimination. You should be able to recognize that a predetermined number of operations
are performed, based on the size of the linear system. So we’ll use for loops, since the
size of the matrix has to be known.
The next challenge is to decide how many for loops are required. This is not a trivial
decision. We must use the numerical example of the previous section to help us out. Try
to identify processes/computations that repeat and attempt to write these computations
as a repeating for loop.

5.1.1 Forward reduction step

Gauss elimination required three nested for loops during the forward reduction step.
Here is what each loop will do:

1. One index (loop) tracks the diagonal term we are currently using to make all other
terms below it zero. This row is called the pivot row and the entry on the diagonal
is called the pivot element.

28
2. A second index (loop) will track which row we are currently working on. All the
rows below the pivot row will be changed by adding or subtracting an appropriate
fraction of the pivot row.

3. The last index (loop) is used to perform the required computation for all the columns
of the rows below the pivot row.

Let’s implement the forward reduction step of Gauss elimination. This will illustrate the
necessity of the three required loops more clearly.

1: % Gauss elimination program that solves x


2: % in the linear system Ax = a.
3: % Create left hand side matrix A
4: A = [ 2 3 4 1
5: 1 1 2 1
6: 2 4 5 2
7: 1 2 3 4 ];
8: % Create right hand side vector a
9: a = [10
10: 5
11: 13
12: 10 ];
13: % Compute size of matrix
14: n = length(A);
15: % Start of 1st loop: pivot row counter
16: for i=1:n-1
17: % Start of second loop: which row to reduce
18: for j=i+1:n
19: % Compute the multiplier i.e. which fraction of the
20: % pivot row i has to be subtracted from row j
21: mult = A(j,i)/A(i,i);
22: % Start of 3rd loop: re-assign all columns of row j
23: % i.e. Row_j = Row_j - mult*Row_i for columns 1 to n
24: for k=1:n
25: A(j,k) = A(j,k) - mult*A(i,k);
26: end
27: % Also modify the RHS vector
28: a(j) = a(j) - mult*a(i);
29: end
30: end
31: A
32: a

In program lines 1–12, the matrix A and vector a are defined. Program line 14 gets the
number of rows of the matrix A and this is assigned to the variable n. The actual forward
reduction step of Gauss elimination starts at program line 16.

29
Since we use the index i to indicate the pivot element, what must the bounds be on this
index? You should be able to recognize that we will start this index at 1 (recall that we
make all the entries of the matrix below the (1,1) entry equal to zero?). After all the
entries below the (1,1) entry are zero, we make all the entries below the (2,2) entry zero
(so the pivot element index = 2). The pivot element index keeps on incrementing by one
till it reaches the value n-1. Does this make sense to you? We have to stop at this value,
because a pivot element of (n,n) makes no sense: there are no entries in the matrix below
the (n,n) entry to make zero. I hope you now understand program line 16 which starts
the pivot element index at 1 and increments with one till the final pivot element index of
n-1.
Program line 18 defines the row index j. It’s initial value is set to i+1, and it increments
with one till a final row index of n. Does this make sense? If the pivot element index is i,
we must reduce all entries below the (i,i) entry to zero. So we start with row i+1 and
continue to row n.
Program line 21 defines the multiplier i.e. which fraction of the i-th row must be subtracted
from the j-th row in order to set the A(j,i) term to zero. The multiplier is simply the
A(j,i) term divided by the pivot element A(i,i).
Program lines 24–26 simply subtracts the appropriate fraction of row i from row j and
assigns this to row j. A for loop is required to repeat this for all columns (1 to n).
Program line 28 performs the same computation on the right hand side vector a.
Note that the order of the three nested for loops is fixed. The outer for loop sets the
pivot row counter (i). Once i is known, the next for loop sets the lower bound of the
row counter j in terms of i. Once i and j are known, the forward reduction step can be
performed on row j for all columns k (the third and final for loop).
Save the above program as Gauss.m. Type Gauss in the Command Window and press
enter. The following will appear:

A =

2.00000 3.00000 4.00000 1.00000


0.00000 -0.50000 0.00000 0.50000
0.00000 0.00000 1.00000 2.00000
0.00000 0.00000 0.00000 2.00000

a =

10
0
3
2

As you can see, the forward reduction step is implemented correctly. The matrix A and
vector a agree with Eq.(14).

30
5.1.2 Backsubstitution step

After we reduced the system to an upper triangular system, we are ready to start the
backsubstitution process. As before, we first have to decide what type of computations
are required. for loops are again required, because the number of operations we have to
perform is directly related to the size of the matrix (which is known).
Just to illustrate the required steps more clearly, consider a general 4 × 4 system that is
already upper-triangular:
    
A11 A12 A13 A14   x 1 
   a 1 

 0 A22 A23 A24   x2   a2 

 0
 = (20)
0 A33 A34    x3  
 a3 
0 0 0 A44 x4 a4
   

The four unknowns x4 , x3 , x2 and x1 are solved in this order from


a4
x4 = (21)
A44
a3 − A34 x4
x3 = (22)
A33
a2 − A23 x3 − A24 x4
x2 = (23)
A22
a1 − A12 x2 − A13 x3 − A12 x2
x1 = (24)
A11
You should be able to recognize the pattern in Eqs.(21)– (24):
an
xn = (25)
Ann
n
X
ai − Aij xj
j=i+1
xi = for i = n − 1, n − 2, . . . , 1. (26)
Aii
From Eq.(26) you should be able to recognize the need for two for loops: i = n − 1, n −
2, . . . , 1 and j = i + 1, i + 2, . . . , n. The i counter points to the current component of the
vector x we are solving, while the j counter keeps track of the sum of products we must
subtract from ai . The order in which the for loops appear is fixed: the outer for loop
must contain the variable i, and the inner for loop the variable j. This is because the j
for loop lower bound is defined in terms of the current value of the i counter.
I’ll go ahead an program the backsubstitution step of the Gauss elimination algorithm.

31: % Define a n x 1 vector of zeros


32: x = zeros(n,1);
31: % Solve the last unknown
32: x(n,1) = a(n)/A(n,n);
33: % Start for loop: solve from row n-1, using
34: % increments of -1 all the way to row 1

31
35: for i=n-1:-1:1
36: % Start computation with the right hand side vector value
37: x(i,1) = a(i);
38: % Now subtract from that the product of A and the
39: % x terms that are already solved: use a for loop
40: for j=i+1:n
41: x(i,1) = x(i,1) - A(i,j)*x(j);
42: end
43: % Now divide by the diagonal A(i,i) to get the x term
44: x(i,1) = x(i,1)/A(i,i);
45: end
46: x

Append the backsubstitution part to the Gauss.m file. Type Gauss in the Command
Window and press enter. The following should appear:

x =
1
1
1
1

which is the correct solution to this 4 × 4 linear system of equations.

5.2 Solving linear systems using Matlab

We’ve just developed our own implementation of Gauss elimination. It can solve any
n × n system of linear equations. I believe that Gauss elimination is quite a challenging
algorithm to understand, so if you manage you are well on your way to becoming a
proficient programmer.
Since Matlab was originally developed to perform linear algebra, you probably expect
that Matlab has built-in capability to solve large linear systems. This is the case. In fact,
more than one method exists to solve linear systems. If the linear system

Ax = a (27)

has a unique solution, the inverse of A exists and we can premultiply Eq.(27) with A−1
to obtain
x = A−1 a (28)
This operation is performed in Matlab using the inv statement i.e.

x = inv(A)*a;

Another Matlab method to solve linear systems is the backslash operation i.e.

32
x = A\a;

solves x using a factorization and backsubstitution algorithm similar to the Gauss elimi-
nation algorithm we implemented.
So let’s compare the efficiency of our algorithm with that of Matlab’s built-in functions.
This can be done by using the tic and toc statements. The tic statement simply starts a
stopwatch, and the toc statement stops the stopwatch and displays the time expired since
the tic statement was executed. So here follows a complete program that solves a 500
× 500 system using the three methods i) our Gauss elimination algorithm, ii) Matlab’s
inv(A)*a method and iii) Matlab’s A\a method.

1: % Create matrix A and vector a


2: A = rand(500,500);
3: a = rand(500,1);
4: % make copies of A and a to use later
5: A_copy = A;
6: a_copy = a;
7: % Start the stopwatch
8: tic
9: % Forward reduction step as before
10: n = length(a);
11: for i=1:n-1
12: for j=i+1:n
13: mult = A(j,i)/A(i,i);
14: for k=1:n
15: A(j,k) = A(j,k) - mult*A(i,k);
16: end
17: a(j) = a(j) - mult*a(i);
18: end
19: end
20: % Backsubstitution step as before
21: x = zeros(n,1);
22: x(n) = a(n)/A(n,n);
23: for i=n-1:-1:1
24: x(i)=a(i);
25: for j=i+1:n
26: x(i) = x(i) - A(i,j)*x(j);
27: end
28: x(i) = x(i)/A(i,i);
29: end
30: % Stops stopwatch and displays time in Command Window
30: toc
31: % Starts stopwatch again
32: tic
33: % Solve system using inverse of A_copy*a_copy
34: x_Mat1 = inv(A_copy)*a_copy;

33
35: % Stops stopwatch and displays time in Command Window
36: toc
37: % Starts stopwatch third and final time
38: tic
39: % Solve system using backslash operation
40: x_Mat2 = A_copy\a_copy;
41: % Stops stopwatch and displays time in Command Window
42: toc

The output of the above program is

elapsed_time =
8.5223

elapsed_time =
0.2420

elapsed_time =
0.1125

for computations on a 2.4 GHz Intel Celeron processor running Matlab Student Version
Release 13 under the Mandrake Linux operating system. As you can see the inverse
method is about 35 times faster than our method and the backslash method is about 75
times faster than our method. The output of the same program on the same computer,
running Matlab 6.1 under the Windows XP operating system is:

elapsed_time =
363.1320

elapsed_time =
0.2700

elapsed_time =
0.1100

Our Gauss elimination program is more than 40 times slower in Matlab 6.1 compared
to Matlab 6.5 (Student Edition)! This is due to a recent major improvement in the
performance of for loops in Matlab. But you’ll notice that Matlab’s built-in functions
perform similarly, independent of the Matlab version. We’ll revisit the performance of
for loops is a later section.
For now, we’ll focus on improving the performance of our algorithm, regardless of which
Matlab version we’re using. We can make a dramatic improvement in our algorithms’
efficiency by making a subtle change during the forward reduction step. Refer to program
line 14 above: you’ll see that we subtract a fraction of line i from line j, for columns 1
to n. But is this really necessary? As we perform the forward reduction step of Gauss
elimination, we know that the entries to the left of the diagonal will become zeros. And

34
we never use these zeros during the backsubstitution step of the algorithm. So why do
we bother computing them at all? Computers take the same amount of time to multiply
zeros with some other number as compared to non-zero number multiplication, so we are
wasting valuable time. All that we have to do to improve our algorithm, is to change
program line 14 to

14: for k=1+i:n

This new program line will only subtract those columns that will end up as non-zeros, so
we aren’t computing the zeros anymore. You can check that this change doesn’t affect
the solution, and the times required to solve a 500 × 500 system now becomes

elapsed_time =
5.0185

using Matlab 6.5 and

elapsed_time =
235.9200

using Matlab 6.1. This simple change has improved the speed of our algorithm by ap-
proximately 40%, regardless of the Matlab version! In the weeks to come we’ll make more
changes to improve the performance of our algorithm.

6 Functions
A function is a list of program statements that perform a specific task. Functions are
called from other programs, or from other functions. We have encountered a number
of Matlab functions so far. The first is the rand functions, which is a function that
can generate random matrices. Do you recall the syntax of the rand statement? The
statement

Matrix = rand(Rows,Columns);

generates a variable Matrix that is a Rows × Columns random matrix. As you can see,
the rand function requires two scalar inputs (in the example above Rows and Columns)
and it generates a single matrix as output (Matrix in the example above).
We have also encountered the inv function, which generates the inverse of a matrix. The
inv functions requires a single square matrix as input and it generates the inverse of this
matrix as output. The statement

A_inv = inv(A);

35
will generate the matrix A inv which is the inverse of the matrix A.
A special type of functions we have used is the inline function, which a function that
generates other functions. The inline function requires a single string variable as input
and it generates a function. This is unusual, since most functions take variables (scalar,
matrices or strings) as input and generate other variables (again scalars, matrices or
strings) as output.
The trigonometric functions sin, cos, tan and the exponential function exp are other
examples of functions that we have used so far. All of these functions require a single
variable as input and it produces a single variable as output (You’ll see in the next section
that the variable may be a vector or matrix).
The final example of a function we have encountered so far is the size function. This
function requires a single input (the matrix A in the example to follow), while it produces
two outputs (the scalars Rows and Columns in the example to follow):

[Rows,Columns]=size(A);

6.1 Creating new functions

Programming will be a very difficult task if we could only make use of existing funtions.
Not only will our programs become very long and difficult to keep track of, but it will
also be impossible for a group of programmers to develop different components of a new
program simultaneously. Fortunately, all programming languages allow for creating new
functions.
In Matlab, new functions are created by storing the list of required programming com-
mands in a seperate file and then indicate that this is a new function by typing

function [Output1,Output2,...] = FunctionName(Input1,Input2,...)

in the very first line of the program. The word function is compulsory, it tells Matlab
that what follows is a function. Then the list of variables (outputs) that the function will
generate follows, enclosed in square brackets. Different outputs are seperated by commas.
These output variables can have any name, as long as you obey the variable name rules.
After the equal sign, the name of your function follows. The function name must also
obey the usual variable and program name rules. The function statement is completed by
the list of input variables, enclosed in parentheses. The function file must be saved using
the same name as the as function itself, FunctionName.m for this example.
The variable names that you use within the function is local variables, i.e. these variable
values are only known inside the function. The values of the variables used in your
function will not be available to the calling program (or in the Command Window). If
you want a variable value to be available to a calling program, send the variable value
back to the calling program by adding this variable to the list of output arguments.
I’ll now give numerous examples of functions. I’ll start of by writing a function that can
add two numbers. My functions will need 2 inputs (the two numbers I want to add) and it

36
will produce a single output (the sum of these two numbers). So I decide that the syntax
of my function Add.m is:

Num3 = Add(Num1,Num2);

where the variable Num3 will be the sum of the variables Num1 and Num2. Now I need to
write the function Add.m:

function [Number_out] = Add(Number1_in,Number2_in)


Number_out = Number1_in + Number2_in;

For this example, the function Add.m contains only 2 program lines. The first line defines
the inputs, outputs and the name of the function. The remaining program lines in a
function computes the outputs, making use of the inputs. In this case a single program
line is sufficient. Notice that inside the function, you assume that the input variables
are available (i.e. these values will be passed in from the calling program, so you should
not define these values). Any other variables that you require during computations have
to be defined within the function. Also note that the variable names you use inside the
function are independent from the variable names you use when calling the function.
You can call the new function Add from any program or other functions. You can also
call the function from the Command Window. I’ll illustrate calling the function Add from
a program Main.m:

1: % Define the inputs that the function Add requires


2: Num1 = 4.0;
3: Num2 = 5.0;
4: % Call the function, passing the input variables Num1 and Num2
5: % into the function and getting the output Num3 back
6: Num3 = Add(Num1,Num2);
7: % Display output
8: disp([’The sum of ’,num2str(Num1),’ and ’,num2str(Num2),...
9: ’ is ’,num2str(Num3)])

The function Add is called in program line 6 and the output of the program listed above
is:

The sum of 4 and 5 is 9

Let’s consider another example. Suppose we want to write a function that takes two
numbers as inputs, and then performs the simple operations addition, subtraction, multi-
plication and division to provide four outputs. I decide the function name is Arithmetic,
so the syntax of such a function is:

[Add,Subtract,Multiply,Divide] = Arithmetic(Num1,Num2)

37
where the two scalar inputs are Num1 and Num2 and the four scalar outputs are Add,
Subtract, Multiply and Divide. The function Arithmetic.m follows:

function [A,S,M,D]=Arithmetic(Number1,Number2)
A = Number1 + Number2;
S = Number1 - Number2;
M = Number1 * Number2;
D = Number1 / Number2;

I can call the above function from programs, other functions or the Command Window.
I’ll illustrate calling the Arithmetic function from the Command Window:

EDU>> [A,B,C,D] = Arithmetic(3.0,4.0)

A =
7

B =
-1

C =
12

D =
0.7500

You should notice that the variable names used to call the function has nothing to do
with the variable names used inside the function.
As the programmer, you can choose whether or not the inputs and outputs of your function
are scalars, vectors or matrices. As an example, let’s assume you prefer that the input to
your Arithmetic function is a single vector, with two components:

function [A,S,M,D]=Arithmetic(Numbers)
A = Numbers(1) + Numbers(2);
S = Numbers(1) - Numbers(2);
M = Numbers(1) * Numbers(2);
D = Numbers(1) / Numbers(2);

Notice that the input is now a single vector variable Numbers that contain two components.
The function Arithmetic cannot be called using two inputs anymore, it now has to be
called using a single vector containing two components as input. The four scalar outputs
could also be replaced by a single vector variable:

function [Outputs]=Arithmetic(Numbers)
Outputs(1) = Numbers(1) + Numbers(2);
Outputs(2) = Numbers(1) - Numbers(2);

38
Outputs(3) = Numbers(1) * Numbers(2);
Outputs(4) = Numbers(1) / Numbers(2);

To illustrate this last version of the Arithmetic function, I called it from the Command
Window:

EDU>> Answer=Arithmetic([3.0 4.0])

Answer =
7.0000 -1.0000 12.0000 0.7500

As you can see, I called the Arithmetic function using the [3 4] vector as input. I
obtained a single vector Answer as output, with the four computed values available in the
four components.
The final simple function we’ll write is a function that computes the components of a
vector, if the length and angle of the vector is provided. Let’s decide that the angle can
be specified in either degrees or radians. I choose the name of the function as Decompose
(meaning a function that decomposes a vector into it’s components). The syntax of the
function is

[Comp_x,Comp_y,Message] = Decompose(Length,Angle,Option)

where the inputs are the length of the vector (length), the vector’s angle (Angle) and the
variable Option, which must distinguish between degrees or radians. The outputs are the
two components Comp x and Comp y, and a string variable Message that can conatain an
error message. As the programmer, you decide how you want to use the Option variable.
You can decide that the Option variable can have a value of 1 or 2, where 1 indicates
the angle is expressed in degrees and 2 indicates the angle is expressed in radians. Or
you can decide that the variable Option is a string variable that can either contain a
’D’ to indicate degrees or a ’R’ to indicate radians. I’ll use the latter option. If the
Option variable doesn’t contain either a ’R’ or ’D’, I set the Message variable equal to
’Incorrect input’ and then use the return statement. The return statement exists
the function immediately and returns to the calling program.
Here follows the function Decompose.m:

1: function [Comp1,Comp2,Message] = Decompose(Magnitude,Angle,Option)


2: % Assign blanks to outputs
3: Message = [];
4: Comp1 = [];
5: Comp2 = [];
6: % Check if the angle is expressed in degrees: if so, convert to radians
7: if Option==’D’
8: Angle = Angle*pi/180;
9: % If Option not equal to ’R’, incorrect input was provided
10: elseif Option~=’R’

39
11: Message=’Incorrect input’;
12: % Since input was incorrect, exit function and return to calling program
13: return
14: end
15: % 1st component the cosine of angle times magnitude
16: Comp1 = Magnitude*cos(Angle);
17: % 2nd component the sine of angle times magnitude
18: Comp2 = Magnitude*sin(Angle);

I called the new function from the Command Window to produce the following output.
I used both options i.e. expressed the angle as 45 degrees and as π4 radians. I also called
the function using incorrect input. As you can see, the first two cases produce the same
output (as it should) and the last case produces the error message.

EDU>> [C1,C2,M]=Decompose(10,pi/4,’R’)
C1 =
7.0711
C2 =
7.0711
M =
[]

EDU>> [C1,C2,M]=Decompose(10,45,’D’)
C1 =
7.0711
C2 =
7.0711
M =
[]

EDU>> [C1,C2,M]=Decompose(10,45,1)
C1 =
[]
C2 =
[]
M =
Incorrect input

6.1.1 Gauss elimination using functions

I’ll revisit the Gauss elimination program to further illustrate the use of functions. As
the programmer, you can decide how to organize your program. In this case, I’ve decided
to make use of two functions. The first, called Reduction, is a function that performs
the forward reduction step of the Gauss elimination process. The second function, called
Backsub, performs the backsubstitution step. The inputs that the forward reduction step
requires is the matrix (A in this case) and the vector (a in this case). The outputs that

40
the forward reduction step produces are the modified matrix A and modified vector a.
Hence the syntax of the Reduction function is

[A,a]=Reduction(A,a);

The Backsub function takes the reduced natrix A and the vector a as input and it produces
the solution to the linear system as output (x in this case). Hence the syntax of the
Backsub function is

[x]=Backsub(A,a);

The final version of the Gauss elimination program listed in Section 5.2 now reads:

1: % Create matrix A and vector a


2: A = rand(500,500);
3: a = rand(500,1);
4: % make copies of A and a to use later
5: A_copy = A;
6: a_copy = a;
7: % Start the stopwatch
8: tic
9: % Call the forward reduction function
10: [A,a] = Reduction(A,a);
11: % Call the backsubstitution function
12: [x] = Backsub(A,a);
13: % Stops stopwatch and displays time in Command Window
14: toc
15: % Starts stopwatch again
16: tic
17: % Solve system using inverse of A_copy*a_copy
18: x_Mat1 = inv(A_copy)*a_copy;
19: % Stops stopwatch and displays time in Command Window
20: toc
21: % Starts stopwatch third and final time
22: tic
23: % Solve system using backslash operation
24: x_Mat2 = A_copy\a_copy;
25: % Stops stopwatch and displays time in Command Window
26: toc

The two functions Reduction.m and Backsub.m follows:

Reduction.m
1: function [Matrix,Vector]=Reduction(Matrix,Vector)
2: n = length(Vector);
3: for i=1:n-1

41
4: for j=i+1:n
5: mult = Matrix(j,i)/Matrix(i,i);
6: for k=1+i:n
7: Matrix(j,k) = Matrix(j,k) - mult*Matrix(i,k);
8: end
9: Vector(j) = Vector(j) - mult*Vector(i);
10: end
11: end

Backsub.m
1: function [Solution]=Backsub(Matrix,Vector)
2: n = length(Vector)
3: Solution = zeros(n,1);
4: Solution(n) = Vector(n)/Matrix(n,n);
5: for i=n-1:-1:1
6: x(i)=Vector(i);
7: for j=i+1:n
8: Solution(i) = Solution(i) - Matrix(i,j)*Vector(j);
9: end
10: Solution(i) = Solution(i)/Matrix(i,i);
11: end

The performance of the new version of the Gauss elimination program is similar to the
original program. This example is useful as an educational example, but is probably not
realistic. I can’t imagine a scenario where we would like to only perform forward reduction
or only backsubstitution. Rather, a single function Gauss makes more sense. It takes the
matrix A and vector a as input and it produces the vector x as output i.e.

[x] = Gauss(A,a);

The main program now reads:

1: % Create matrix A and vector a


2: A = rand(500,500);
3: a = rand(500,1);
4: % Start the stopwatch
5: tic
6: % Call the Gauss elimination function
7: [x] = Gauss(A,a);
8: % Stops stopwatch and displays time in Command Window
9: toc
10: % Starts stopwatch again
11: tic
12: % Solve system using inverse of A*a
13: x_Mat1 = inv(A)*a;
14: % Stops stopwatch and displays time in Command Window

42
15: toc
16: % Starts stopwatch third and final time
17: tic
18: % Solve system using backslash operation
19: x_Mat2 = A\a;
20: % Stops stopwatch and displays time in Command Window
21: toc

and the function Gauss reads:

1: function [Solution]=Gauss(Matrix,Vector)
2: n = length(Vector);
3: for i=1:n-1
4: for j=i+1:n
5: mult = Matrix(j,i)/Matrix(i,i);
6: for k=i+1:n
7: Matrix(j,k) = Matrix(j,k) - mult*Matrix(i,k);
8: end
9: Vector(j) = Vector(j) - mult*Vector(i);
10: end
11: end
12: Solution = zeros(n,1);
13: Solution(n) = Vector(n)/Matrix(n,n);
14: for i=n-1:-1:1
15: x(i)=Vector(i);
16: for j=i+1:n
17: Solution(i) = Solution(i) - Matrix(i,j)*Vector(j);
18: end
19: Solution(i) = Solution(i)/Matrix(i,i);
20: end

6.1.2 Numerical integration using functions

I conclude this section on functions by giving a final example. Consider the numerical
integration program in Section 1. I propose that each of the four methods of integration
can become a function. So all we have to determine is the inputs and outputs of these
functions. As the programmer, I decide that as outputs I want the numerical value of the
integral as well as the required number of intervals. Regarding the inputs, here we have
no choice. A function that must perform numerical integration needs i) the function to
be integrated, ii) the lower and upper bounds of integration iii) the number of intervals
and iv) the required accuracy. All other values can be defined within the function. So
the syntax of the integration functions is

[Int,N]=Leftpoint(Fcn,LB,UB,N_ini,Acc);

where the inputs are the functions Fcn, the lower bound LB, upper bound UB, initial
number of intervals N ini and the required accuracy Acc. The function will then provide

43
the intergal Int and the required number of intervals N. Here’s the listing of the modified
numerical integration program Integrate.m and only the Leftpoint.m function. The
other functions only differ in the details of the area computation.

Integrate.m
1: % Get user inputs
2: F_string = input(’Please enter function to be integrated. ’,’s’);
3: Fcn = inline(F_string);
4: LB = input(’Enter the lower bound of integration. ’);
5: UB = input(’Enter the upper bound of integration. ’);
6: N_ini = input(’Initial number of intervals between LB and UB? ’);
7: acc = input(’Enter the required accuracy. ’);
8: disp(’1: Left-point 2: Right-point 3: Mid-point’);
9: disp(’4: Trapezium 5: Quit program’);
10: Method = input(’Please enter your option. ’);
11: % My program repeats until the user inputs option 5
12: while Method~=5
13: % Depending on method chosen, call appropriate function
14: switch Method
15: case 1
16: [Int,N]=Leftpoint(Fcn,LB,UB,N_ini,acc);
17: case 2
18: [Int,N]=Rightpoint(Fcn,LB,UB,N_ini,acc);
19: case 3
20: [Int,N]=Midpoint(Fcn,LB,UB,N_ini,acc);
21: case 4
22: [Int,N]=Trapezium(Fcn,LB,UB,N_ini,acc);
23: end
24: % Get length of the integration vector
25: k = length(Int);
26: % Display answer
27: disp([’Integrating ’,F_string,’ between ’,num2str(LB),’ and ’, ...
28: num2str(UB)])
29: disp(’Intervals Integral’);
30: % Display first k components of vectors N and Int
31: disp([num2str(N(1:k)’,’%8d\t\t’),num2str(Int(1:k)’,’%10.6f’)])
32: % Display options again
33: disp(’1: Left-point 2: Right-point 3: Mid-point’);
34: disp(’4: Trapezium 5: Quit program’);
35: Method = input(’Please enter your option. ’);
36: end; %End of outer while loop
37: % Outside while loop, user entered option 5 = Quit
38: % Displays a friendly message.
39: disp(’Thanks for using the program. Have a good day.’)

Leftpoint.m
1: function [Int,N]=Leftpoint(Fcn,LB,UB,N_ini,acc)

44
2: % Define initial Error greater as acc, to enter while loop
3: Error = 2*acc;
4: % Number of intervals is saved in a vector N. I start with component 2
5: % to correspond to the integrals in the vector Int
6: N(2) = N_ini;
7: % Set counter k to one
8: k = 1;
9: % Set component 1 of the Int vector to zero.
10: Int(1) = 0;
11: % Start while loop that checks if consecutive values converged
12: while Error>acc
13: % Increment the counter k with one
14: k = k + 1;
15: % Initialize the current component of the Int vector to zero
16: Int(k) = 0;
17: % Compute the interval size Delta_x
18: Delta_x = (UB-LB)/N(k);
19: % For loop to compute area of each rectangle and add it to Int
20: for i=1:N(k)
21: x = LB + (i-1)*Delta_x;
22: Int(k) = Int(k) + Delta_x*Fcn(x);
23: end
24: Error = abs(Int(k) - Int(k-1));
25: % Component k+1 of N vector = 2 times component k value
26: N(k+1) = 2*N(k);
27: end; % End of inner while

6.1.3 Persistent variables

The local variables inside your function cease to exist once the function is executed and
returns to the calling program. However, in certain cases you may want the value of your
local function variables to be remembered whenever the function is called again. This
can be achieved by declaring a variable as persistent. Immediately after your function
definition line, include the line

persistent VariableName

to declare the variable VariableName as persistent. Persistent variables can save a lot of
time in certain cases, where we don’t have to recompute values that have been computed
before. A simple example is to set a counter to zero and increment it by one every time
a function is called. We can use this idea to count how many times a function is called
when we perform numerical integration.

45
6.1.4 Global variables

The basic philosophy of functions is that they communicate with the calling program
only through the input and output arguments. The function only has access to the values
passed to it from the calling program, and the calling program only receives the function
output. In very rare circumstances, it might be required that a function must also have
access to a variable that is not passed in as one of the arguments. In such a case, you
make use of the global statement.
Whenever the need arises in a function to know the value of some variable, but for some
reason that value cannot be passed to the function as part of the input variables, that
variable is declared as a global variable in the second line of the function. As an example,
consider the function Gauss in Section 1.1.1. Let’s assume that we do not want to pass
the size of the matrix n in as an argument, but we also do not want to recompute it using
the length statement. Then we can define n as a global variable as follows:

function [Solution]=Gauss(Matrix,Vector)
global n
for i=1:n-1
:

The variable n must also be defined as a global variable in the calling program, so the
main program’s first line now reads

global n

Now, the value of the variable n in known at all times in the main program and the
function Gauss. Should the value of n be changed inside the function Gauss, the value
will also be changed inside the main program. So the basic philosophy of functions no
longer holds, since a computation inside a function can affect the value of a variable
outside that function. Because of this, the use of global variables is strongly discouraged.
If you really require access to a variable value inside some function, rather pass that value
in as one of the input variables.

7 Vectorizing algorithms
Although we are using Matlab in this programming course, I believe that the material
I have presented so far is general in that the ideas are common to most programming
languages. The remainder of this course will however be Matlab specific.
One of the most important characteristics that sets Matlab apart from most other pro-
gramming languages is it’s ability to deal with vectors and matrices. This goes back to the
original purpose of Matlab i.e. to perform linear algebra on large systems. So it should be
no surprise that Matlab in fact ‘prefers’ to perform vector and matrix operations rather
than scalar operations. What I mean with ‘prefer’ is that Matlab is more efficient when it

46
performs vector and matrix operations compared to the same operations performed com-
ponent by component. So whenever we program in Matlab, we should avoid component
by component vector and matrix computations using for loops. Rather, ‘vectorize’ the
computations, as will be shown in this section.

7.1 Creating vectors and vector computations

So far in this course, whenever we created a vector we used a for loop to assign the
components. For example, a vector x containing 100 equal intervals between a lower
bound LB and an upper bound UB is created by

LB = -10;
UB = 10;
for i=0:100
x(i+1) = LB + i*(UB-LB)/100;
end

Instead of using the above program, there are two more efficient methods in Matlab to
create evenly spaced vectors. In the first method, the start value, increment and end
value is specified i.e. the statement

x = Start:Increment:End;

will create a vector x with the first component equal to Start, the second component
equal to Start+Increment, the third component equal to Start+2*Increment etc. with
the last component equal to or less than End if Increment>0, or equal to or greater than
End if Increment<0. Consider the following examples I ran from the Command Window:

EDU>> x=0:1:5
x =
0 1 2 3 4 5

EDU>> x=0:2:5
x =
0 2 4

EDU>> x=2:-0.5:0
x =
2.0000 1.5000 1.0000 0.5000 0

EDU>> x=2:-0.7:0
x =
2.0000 1.3000 0.6000

So the program on page 1 that uses a for loop can be replaced with:

47
LB = -10;
UB = 10;
x = LB:0.2:UB;

Another built-in Matlab method to create linearly spaced vectors is the linspace com-
mand. Here we specify the start value, end value and the total number of datapoints. For
the example considered here (LB=-10, UB=10), the statement

x = linspace(LB,UB,101);

is equivalent to the program line x = LB:0.2:UB;


Now that we know how to create vectors efficiently, I’ll show you how to perform vector
computations. Let’s assume we want to plot the function

y = sin(x) for x ∈ [−π, π] (29)

as was considered in Section 2.5. Before we can plot the function, we have to define a x
vector from −π to π. Then we compute the associated y vector by simply taking the sin
of the x vector. Compare the program

x = linspace(-pi,pi,100);
y = sin(x);
plot(x,y)

with the program in Section 2.5. Experiment with other functions and produce their
graphs.

7.1.1 Linear algebra vs. component-by-component computations

You’ve just witnessed how we can perform a computation on a complete vector to produce
another vector. You might think that we can use these methods to produce the graph of
y = x2 , let’s say for x ∈ [−2, 2]. The program

x = linspace(-2,2,100);
y = x^2;
plot(x,y)

will however produce the error statement

??? Error using ==> ^


Matrix must be square.

or if program line 2 is y = x*x; the error statement reads

??? Error using ==> *


Inner matrix dimensions must agree.

48
So why aren’t these statements correct? Recall that Matlab was originally developed to
perform linear algebra. So Matlab interprets statements such as x^2 or x*x in terms of
linear algebra operations. In the above example, the vector x is defined as a 1 × 100
vector. Therefore the statement x*x is interpreted as the multiplication of a 1 × 100
vector with another 1 × 100 vector, which is not defined. Therefore the error message
Inner matrix dimensions must agree.
But when we want to calculate the associated y-values for a x vector between -2 and 2, we
don’t want linear algebra operations to be performed. Rather, we want to calculate a y
vector where every component is simply equal to the square of the associated x component.
In Matlab, this type of computation is performed by using a period immediately before the
multiplication symbol. Whenever a mathematical operation is preceded by a period (.),
Matlab is given instruction to perform that operation component by component, instead
of the usual linear algebra rules. So the correct method to compute the y vector is:

x = linspace(-2,2,100);
y = x.^2; or y = x.*x;
plot(x,y)

Let’s consider another example. Suppose I want to plot the function


e2x
y = ex sin(πx) − for x ∈ [0, 2] (30)
cos(2πx)
The appropriate Matlab program is:

x = linspace(0,2,100);
y = exp(x).*sin(pi*x) - exp(2*x)./(cos(2*pi*x));
plot(x,y)

I’ll now explain the use of the periods: The result of the computation exp(x) is a vector,
as is the result of the computation sin(pi*x). So a period is required before the multi-
plication sign, to prevent Matlab from attempting linear algebra multiplication. Similarly
the result of the exp(2*x) computation is a vector, as is the result of the cos(2*pi*x)
computation. Standard division of these two vectors aren’t even defined, so the period
preceding the division symbol (/) tells Matlab to perform this operation component by
component. Notice that addition and subtraction never requires a period, since vector
and matrix addition and subtraction are performed component by component.
As a final example of the use of the period, consider the following:

A = [1 2
3 4];
B = A*A;
C = A.*A;

The period operation is interpreted exactly the same when dealing with matrices. Since
the matrix A is square, the operation A*A is defined as usual matrix-matrix multiplication
and will result in

49
B =
6 10
15 20

The operation A.*A will perform multiplication component by component, resulting in

C =
1 4
9 16

7.2 Vectorization

‘Vectorization’ of an algorithm means that instead of computing a vector or matrix com-


ponent by component by making use or for loops (or nested for loops), we attempt to
compute a complete vector with a single program line (or compute a complete row or
column in a matrix by using a single program line). This is frequently possible, subject
to certain limitations.
The condition that has to be true in order to vectorize a computation, is that the particular
components that are computed as the for loop proceeds do not depend on the value of
the preceding components. If this is the case, vectorization is not possible since all the
components cannot be computed simulataneously.
The reason for vectorizing an algorithm is purely for computational speed. Older versions
of Matlab are notorious for the slow execution of for loops, but the recent releases are
much improved. So you will have to experiment with your particular Matlab version and
see what the effect is when you vectorize an algorithm.

7.2.1 Vectorizing Gauss elimination


To illustrate vectorization, I’ll revisit the Gauss elimination program of Section 5.2. I’ve
already incorporated the change in program line 14, to avoid computing the zeros to the
left of the diagonal.

1: % Create matrix A and vector a


2: A = rand(500,500);
3: a = rand(500,1);
4: % make copies of A and a to use later
5: A_copy = A;
6: a_copy = a;
7: % Start the stopwatch
8: tic
9: % Forward reduction step as before
10: n = length(a);
11: for i=1:n-1
12: for j=i+1:n
13: mult = A(j,i)/A(i,i);
14: for k=1+i:n
50
15: A(j,k) = A(j,k) - mult*A(i,k);
16: end
17: a(j) = a(j) - mult*a(i);
18: end
19: end
20: % Backsubstitution step as before
21: x = zeros(n,1);
22: x(n) = a(n)/A(n,n);
23: for i=n-1:-1:1
24: x(i)=a(i);
25: for j=i+1:n
26: x(i) = x(i) - A(i,j)*x(j);
27: end
28: x(i) = x(i)/A(i,i);
29: end
30: % Stops stopwatch and displays time in Command Window
30: toc
31: % Starts stopwatch again
32: tic
33: % Solve system using inverse of A_copy*a_copy
34: x_Mat1 = inv(A_copy)*a_copy;
35: % Stops stopwatch and displays time in Command Window
36: toc
37: % Starts stopwatch third and final time
38: tic
39: % Solve system using backslash operation
40: x_Mat2 = A_copy\a_copy;
41: % Stops stopwatch and displays time in Command Window
42: toc

I’ll now start eliminating for loops. Whenever nested for loops are used, it is usually
best to start at the innermost for loop since the innermost for loop is executed most
often.
Let’s start at the forward reduction step. Three nested for loops are used here. I’ll first
attempt to eliminate the innermost loop. Before we do so, we have to check whether or
not the computations performed in this loop depend on one another. If so, we will not be
able to vectorize.
The for loop in program lines 14–16 subtracts the appropriate fraction of row i from row
j, from the index k refers to the column: from i+1 to n. The various column computations
do not depend on one another, so we can vectorize. This is achieved by simply substituting
the lines

14: for k=1+i:n


15: A(j,k) = A(j,k) - mult*A(i,k);
16: end

51
with the single program line

A(j,1+i:n) = A(j,1+i:n) - mult*A(i,1+i:n);

In essence, we replace the index k which started at 1+i and ended at n with the vector
1+i:n. After this simple change, the time required to solve a 500 × 500 system reduces
from 235.92 seconds (Matlab 6.1) to:

elapsed_time =
15.9430

but increases from 5.0185 seconds (Matlab 6.5) to

elapsed_time =
9.4705

So eliminating the innermost for loop during the forward reduction step reduces com-
putational time by 93% using Matlab 6.1, but increases the computational time by 89%
using Matlab 6.5. This clearly illustrates the difference between Matlab versions, as well
as the potential benefit (or downside) of vectorization. So we eliminate the innermost for
loop when we use Matlab 6.1, but we retain it if we use Matlab 6.5.
The for loop is program line 12 can also be removed by vectorization. We replace all j’s
in program lines 13-17 with 1+i:n. The forward reduction step now reads:

% Forward reduction step as before


n = length(a);
for i=1:n-1
mult = A(i+1:n,i)/A(i,i);
A(i+1:n,1+i:n) = A(i+1:n,1+i:n) - mult*A(i,1+i:n);
a(i+1:n) = a(i+1:n) - mult*a(i);
end

if we use Matlab 6.1 (both for loops eliminated with vectorization), or we retain the
innermost for loop if we use Matlab 6.5 and only eliminate the second for loop:

% Forward reduction step as before


n = length(a);
for i=1:n-1
mult = A(i+1:n,i)/A(i,i);
for k=1+i:n
A(i+1:n,k) = A(i+1:n,k) - mult*A(i,k);
end
a(i+1:n) = a(i+1:n) - mult*a(i);
end

52
These modifications reduce the time to solve a 500 × 500 linear system from 15.943 seconds
to 5.1570 seconds in the case of Matlab 6.1, and from 5.0185 seconds to 3.3328 seconds if
we use Matlab 6.5.
The outermost for loop during the forward reduction step cannot be eliminated, since all
the columns cannot be reduced to zero’s below the diagonal simultaneously.
The last for loop we can eliminate is the inner for loop used during the backsubstitution
step. As before, we simply replace all j’s with i+1:n. The backsubstitution step now
reads:

% Backsubstitution step as before


x = zeros(n,1);
x(n) = a(n)/A(n,n);
for i=n-1:-1:1
x(i) = (a(i) - A(i,i+1:n)*x(i+1:n))/A(i,i);
end

This change further reduces the time required to solve a 500 × 500 linear system from
5.1570 seconds to only 4.4570 seconds using Matlab 6.1. In the case of Matlab 6.5, the
time is increased slightly from 3.3328 seconds to 3.4717 seconds.
In conclusion, depending on the version of Matlab you are using, vectorization can greatly
increase the speed of program execution. Every vectorization leads to greater efficiency in
the case of Matlab 6.1, but only certain vectorizations are beneficial in the case of Mat-
lab 6.5. For the example considered here, execution time decreased from 235.92 seconds to
4.457 seconds for Matlab 6.1, and from 5.0185 seconds to 3.3328 seconds for Matlab 6.5.

7.2.2 Vectorizing numerical integration

As a last example of vectorization, I’ll illustrate how to vectorize the numerical integration
program presented in Section 1. I’m listing the program again for convenience:

1: % Get user inputs


2: F_string = input(’Please enter function to be integrated. ’,’s’);
3: Fcn = inline(F_string);
4: LB = input(’Enter the lower bound of integration. ’);
5: UB = input(’Enter the upper bound of integration. ’);
6: N_ini = input(’Initial number of intervals between LB and UB? ’);
7: acc = input(’Enter the required accuracy. ’);
8: disp(’1: Left-point 2: Right-point 3: Mid-point’);
9: disp(’4: Trapezium 5: Quit program’);
10: Method = input(’Please enter your option. ’);
11: % My program repeats until the user inputs option 5
12: while Method~=5
13: % Define initial Error greater as acc, to enter while loop
14: Error = 2*acc;
15: % Number of intervals is saved in a vector N. I start with component 2

53
16: % to correspond to the integrals in the vector Int
17: N(2) = N_ini;
18: % Set counter k to one
19: k = 1;
20: % Set component 1 of the Int vector to zero.
21: Int(1) = 0;
22: % Start while loop that checks if consecutive values converged
23: while Error>acc
24: % Increment the counter k with one
25: k = k + 1;
26: % Initialize the current component of the Int vector to zero
27: Int(k) = 0;
28: % Compute the interval size Delta_x
29: Delta_x = (UB-LB)/N(k);
30: % Use the switch statement to branch to one of the 4 methods
31: switch Method
32: % Case 1: left-point method
33: case 1
34: % For loop to compute area of each rectangle and add it to Int
35: for i=1:N(k)
36: x = LB + (i-1)*Delta_x;
37: Int(k) = Int(k) + Delta_x*Fcn(x);
38: end
39: % Case 2: right-point method
40: case 2
41: for i=1:N(k)
42: x = LB + i*Delta_x;
43: Int(k) = Int(k) + Delta_x*Fcn(x);
44: end
45: % Case 3: mid-point method
46: case 3
47: for i=1:N(k)
48: x = LB + (i-1)*Delta_x + 0.5*Delta_x;
49: Int(k) = Int(k) + Delta_x*Fcn(x);
50: end
51: % Case 4: trapezium method
52: case 4
53: for i=1:N(k)
54: x_L = LB + (i-1)*Delta_x;
55: x_R = x_L + Delta_x;
56: Int(k) = Int(k) + 0.5*Delta_x*(Fcn(x_L) + Fcn(x_R));
57: end
58: end; % End of switch
59: % Compute Error i.e. difference between consecutive integrals
60: Error = abs(Int(k) - Int(k-1));
61: % Component k+1 of N vector = 2 times component k value
62: N(k+1) = 2*N(k);

54
63: end; % End of inner while
64: % Display answer
65: disp([’Integrating ’,F_string,’ between ’,num2str(LB),’ and ’, ...
66: num2str(UB)])
67: disp(’Intervals Integral’);
68: % Display first k components of vectors N and Int
69: disp([num2str(N(1:k)’,’%8d\t\t’),num2str(Int(1:k)’,’%10.6f’)])
70: % Display options again
71: disp(’1: Left-point 2: Right-point 3: Mid-point’);
72: disp(’4: Trapezium 5: Quit program’);
73: Method = input(’Please enter your option. ’);
74: end; %End of outer while loop
75: % Outside while loop, user entered option 5 = Quit
76: % Displays a friendly message.
77: disp(’Thanks for using the program. Have a good day.’)

This program can be vectorized by eliminating the for loops in program lines 35, 41, 47
and 53. Program lines 35 to 38 is replaced by

35: x = LB:Delta_x:UB-Delta_x;
36: Heights = Fcn(x);
37: Areas = Delta_x*Heights;
38: Int(k) = sum(Areas);

As you can see, we now compute the complete vector x in program line 35 instead of it’s
individual components. The vector x starts at LB, increments with Delta x and ends at
UB-Delta x. The rectangle heights is then evaluated by passing the complete vector x to
the function Fcn. Multiplying with the rectangle width Delta x gives all the rectangle
areas in the vector variable Areas. I then use the sum statement in program line 38 to sum
all the components of Areas to obtain the integral Int. Using Matlab 6.5, the leftpoint
method (which I have already modified) takes 0.0167 seconds to integrate sin(x) between
0 and π2 for an accuracy of 0.0001 and 10 initial intervals, compared to 12.1847 seconds
before vectorization!
You can practise vectorization by eliminating the other for loops in this program.

55

Вам также может понравиться