Skip to article frontmatterSkip to article content

Algorithms

An idealized mathematical problem f(x)f(x) can usually only be approximated using a finite number of steps in finite precision. A complete set of instructions for transforming data into a result is called an algorithm. In most cases it is reasonable to represent an algorithm by another mathematical function, denoted here by f~(x)\tilde{f}(x).

Even simple problems can be associated with multiple algorithms.

1.3.1Algorithms as code

Descriptions of algorithms may be presented as a mixture of mathematics, words, and computer-style instructions called pseudocode, which varies in syntax and level of formality. In this book we use pseudocode to explain the outline of an algorithm, but the specifics are usually presented as working code.

Of all the desirable traits of code, we emphasize clarity the most after correctness. We do not represent our programs as always being the shortest, fastest, or most elegant. Our primary goal is to illustrate and complement the mathematical underpinnings, while occasionally pointing out key implementation details.

As our first example, Function 1.3.1 implements an algorithm that applies Horner’s algorithm to a general polynomial, using the identity

p(x)=c1+c2x++cnxn1=(((cnx+cn1)x+cn2)x++c2)x+c1.\begin{split} p(x) &= c_1 + c_2 x + \cdots + c_n x^{n-1} \\ &= \Bigl( \bigl( (c_n x + c_{n-1}) x + c_{n-2} \bigr) x + \cdots +c_{2} \Bigr)x + c_{1}. \end{split}

The quoted lines at the beginning of Function 1.3.1 are a documentation string. The function itself starts off with the keyword function, followed by a list of its input arguments. The first of these is presumed to be a vector, whose length can be obtained and whose individual components are accessed through square bracket notation. After the computation is finished, the return keyword indicates which value or values are to be returned to the caller.

The Polynomials package for Julia provides its own fast methods for polynomial evaluation that supersede our simple Function 1.3.1 function. This will be the case for all the codes in this book because the problems we study are well-known and important. In a more practical setting, you would take implementations of basic methods for granted and build on top of them.

1.3.2Writing your own functions

Any collection of statements organized around solving a type of problem should probably be wrapped in a function. One clue is that if you find yourself copying and pasting code, perhaps with small changes in each instance, you should probably be writing a function instead.

Julia
MATLAB
Python

Functions can be defined in text files with the extension .jl, at the command line (called the REPL prompt), or in notebooks.

As seen in Function 1.3.1, one way to start a function definition is with the function keyword, followed by the function name and the input arguments in parentheses. For example, to represent the mathematical function esinxe^{\sin x}, we could use

function myfun(x)
    s = sin(x)
    return exp(s)
end

The return statement is used to end execution of the function and return one or more (comma-separated) values to the caller of the function. If an executing function reaches its end statement without encountering a return statement, then it returns the result of the most recent statement, but this is considered poor style.

For a function with a short definition like the one above, there is a more compact syntax to do the same thing:

myfun(x) = exp(sin(x))

You can also define anonymous functions or lambda functions, which are typically simple functions that are provided as inputs to other functions. This is done with an arrow notation. For example, to plot the function above (in the Plots package) without permanently creating it, you could enter

plot(x -> exp(sin(x)), 0, 6)

As in most languages, input arguments and variables defined within a function have scope limited to the function itself. However, they can access values defined within an enclosing scope. For instance:

mycfun(x) = exp(c*sin(x))
c = 1;  mycfun(3)   # returns exp(1*sin(3))
c = 2;  mycfun(3)   # returns exp(2*sin(3))

There’s a lot more to be said about functions in Julia, but this is enough to get started.

1.3.3Exercises

  1. ⌨ Write a function poly1(p) that returns the value of a polynomial p(x)=c1+c2x++cnxn1p(x) = c_1 + c_2 x + \cdots + c_n x^{n-1} at x=1x=-1. You should do this directly, not by a call to or imitation of Function 1.3.1. Test your function on r(x)=3x3x+1r(x)=3x^3-x+1 and s(x)=2x2xs(x)=2x^2-x.

  2. ⌨ In statistics, one defines the variance of sample values x1,,xnx_1,\ldots,x_n by

    s2=1n1i=1n(xix)2,x=1ni=1nxi.s^2 = \frac{1}{n-1} \sum_{i=1}^n (x_i - \overline{x})^2, \qquad \overline{x} = \frac{1}{n} \sum_{i=1}^n x_i.

    Write a function samplevar(x) that takes as input a vector x of any length and returns s2s^2 as calculated by the formula. You should test your function on the vectors ones(100) and rand(200). If you enter using Statistics in Julia, then you can compare to the results of the var function.

  3. ⌨ Let x and y be vectors whose entries give the coordinates of the nn vertices of a polygon, given in counterclockwise order. Write a function polygonarea(x,y) that computes and returns the area of the polygon using this formula based on Green’s theorem:

    A=12k=1nxkyk+1xk+1yk.A = \frac{1}{2} \left| \sum_{k=1}^n x_k y_{k+1} - x_{k+1}y_k \right|.

    Here nn is the number of polygon vertices, and it’s understood that xn+1=x1x_{n+1}=x_1 and yn+1=y1y_{n+1}=y_1. (Note: The function abs computes absolute value.) Test your functions on a square and an equilateral triangle.