Functional Programming and Intelligent Algorithms

Tutorial 1.2: Recursion and Problem Solving

(Sist oppdatert: 1 January 2015)

Menu

Overview

Comments on the relationship between binary operators (e.g. +) and functions in two arguments.

Problem 1: Bisection Method

In this tutorial we shall implement the Bisection Algorithm which is a simple numerical approach to equation solving. Consider the following equation as an example

0 = x5 + x4·2 + x2·4 + x - 10

Let's not discuss whether a solution can be found analytically. We want to find some solution numerically. To facilitate discussion, we define a symbol f(x) for the right hand side, i.e.

f(x) = x5 + x4·2 + x2·4 + x - 10

A couple of observations are easy to make.

  1. f(-10)<0
  2. f(10)>0
  3. f(x) is continuous

It follows from these three observations that f(x) has at least one root in the interval -10< x<10. Without considering the possibility of multiple roots, we want to find one such root.

You can look up the Bisection Method in most undergraduate introductions to calculus. Let f(x) be a continuous function and (l,u) an interval so that f(l) and f(u) have different sign; then f(x) has a zero in the interval f(x). The Bisection Method finds the midpoint m in the interval (l,u). If f(l) and f(m) have different sign, then there must be a zero in the interval (l,m), and we use bisection recursively on this interval. Otherwise, we call it recursively on the interval (m,u).

Tasks

  1. Open your editor and create a new Haskell module for this problem. Choose an appropriate name for the module.
  2. Implement the function f(x) described above in Haskell. The type description should be f :: Double -> Double
  3. We are going to implement a bisect function, which can be used to find a zero of f with a call like this: bisect (-10) 10 The arguments are resp. the lower and upper bound of the search interval and have type Double.
  4. The bisectfunction has to be a recursive function, so we need two cases. Use l and u to denote the bound of the search interval (l,u).
    Base case
    If the difference l-u is very small, we can just take the average of l and u as the approximate solution. Write a guarded expression for the base case, e.g. bisect u l | u-l < eps = ... choose an appropriate value for eps and add a defintion after the equal sign.
    Recursive case
    If the search interval is larger, we split it in two, and find which half must contain a root. Then we recursively continue the search in the relevant half. The recursive call, with two different cases using guards, can look something like this. bisect u l | fl*fm < 0 = bisect f l m | otherwise = bisect f m u where ... You need to complete the where clause to define the midpoint m and the function values fm (f(m)) and fl (f(l)).
  5. Start ghci and load the module that you have written.
  6. Test your bisection function by finding the root of f(x) in the interval (-10,10). I.e. evaluate the following bisect (-10) 10
  7. It will be useful to check that the result is reasonable. You probably have some software (spreadsheet, MatLab, python, gnuplot) where you can plot f(x) on the interval (-10,10) and check that the zero you find is (approximately) correct. Or you could evaluate the function on the x-point returned by bisect and check if you get approximately zero.

Refinement with higher-order functions

We are going to implement a bisect function, so that it can be used to find zeros of arbitrary continuous functions.

  1. We want to change the bisect function so that it can be used like this bisect f (-10) 10 The first argument is the function for which we want to find a zero, with type Double -> Double, making bisect a higher-order function. The other two arguments are as before.

    1. What is a higher-order function?
    2. Change the type signature of bisect into your module file.
  2. Update the implementation of bisect to become a higher-order function.

    1. Add the function argument f as the first argument on the right hand side of the definition?

    When the symbol f is used as an argument, f on the right hand side will refer to the argument and not to the global definition. Thus no changes are required on the right hand side.

  3. Test the revised bisection function by finding the root of f(x) in the interval (-10,10). I.e. evaluate the following bisect f (-10) 10

Problem 2: More Pictures

Do Exercises 4.25-4.30 in Simon Thompson's book.


Hans Georg Schaathun / hasc@hials.no