Programming made easy (ix) – mortgage payments

At some point in time, everyone has to borrow some money to buy something. Often the biggest purchase to be made by anyone is buying a house. One of the ways of borrowing money is by means of a fixed rate mortgage, i.e. a fixed monthly payment is paid until the loan is paid off with interest at the end of its term.

mp = rP(1+r)n / ((1+r)n-1)

mp = monthly payment
r = monthly interest rate (The monthly rate is calculated by
    dividing the yearly interest rate by 12. e.g. 5% = 0.05/12 
    = 0.0042)
n = number of monthly payments (loan term)
P = principal amount borrowed

The Fortran program shown below calculates the monthly payments for a range of yearly interest rates, X..Y, e.g. 2.0→5.0, in 0.5% increments. Half the program is take up with user input, the remainder prints out a table of monthly mortgage payments for each of the rates.

program mortgage
   real :: p, mp, r, rlow, rhigh, incr, rate
   integer :: nyrs, n
   write(*,*) "mortgage principal: "
   read(*,*) p
   write(*,*) "low interest rate (%/year): "
   read(*,*) rlow
   write(*,*) "high interest rate (%/year): "
   read(*,*) rhigh
   write(*,*) "loan period (years): "
   read(*,*) nyrs

   n = nyrs * 12
   incr = 0.5
   write(*,*) 'Interest rate (%)  Monthly payment($)'
   rate = rlow
   do while (rate <= rhigh)
      r = (rate/100.0)/12.0
      mp = p * r * ((1+r)**n)/((1+r)**n-1)
      write(*,10) rate, mp
      rate = rate + incr
   end do
   10 format(5x,f5.2,15x,f7.2)
end program  mortgage

There isn’t anything vastly different in this program, except the format statement on Line 23. This basically just formats the output in the write statement on Line 20. It prints 5 spaces (5x), followed by a real number of length 5, with 2 decimal places (f5.2), followed by 15 spaces (15x), and finally a real number of length 7 with 2 decimal places (f7.2). This is shown visually below – the X relates to spaces, the f to real numbers (there are other codes for integers, and characters).

Here is a sample run of the program.

 mortgage principal:
100000
 lowest interest rate (%/year):
1.5
 highest interest rate (%/year):
4.0
 number of years:
25
 Interest rate (%)  Monthly payment($)
      1.50                399.93
      2.00                423.86
      2.50                448.63
      3.00                474.21
      3.50                500.62
      4.00                527.84

For comparison, here is the same program written in Python.

p = float(input('mortgage principal: '))
rlow = float(input('low interest rate (%/year): '))
rhigh = float(input('high interest rate (%/year): '))
nyrs = int(input('loan period (years): '))
n = nyrs * 12
incr = 0.5
print('Interest rate (%)  Monthly payment($)')
rate = rlow
while (rate <= rhigh):
    r = (rate/100.0)/12.0
    mp = p * r * ((1+r)**n)/((1+r)**n-1)
    print("    %5.2f                %5.2f" % (rate,mp))
    rate = rate + incr

The only real difference between the Python and Fortran programs is that Python compresses the part of the program dealing with input input one statement per input, as opposed to two. The other problem is that Python doesn’t really “feel” like a program, just a bunch of statements.

Recursion – the basics of tail recursion

In typical recursion, recursive calls are made first, and any return values are used to calculate a result. Therefore, the final result can only be calculated after all the recursive calls have returned some value, which isn’t the most efficient way of doing things recursively.

Now consider a situation where the recursive call is the last action within a subprogram. Recursion is typically implemented using a stack to maintain local information (e.g. variables) pertinent to each recursive call as a stack frame. When a recursive call is initiated a frame is created on the stack. When the recursive call terminates, the frame is removed from the stack and the local information restored. But doing this is pointless, as the recursive call was the last action of the subprogram, so the subprogram terminates and the newly-restored local information is discarded. In this situation it is pointless to use a stack, since no local information needs to be preserved.

This special case of linear recursion when a recursive call is the last executed statement of a subprogram is called tail recursion. In a function that uses tail recursion, the calculations are performed first, and the recursive calls are made last. For example, consider the following C function, sumVector(), which sums the elements in an array. Summing the vector requires looking at each element of the array, which means that for an array of length n, the function sumVector() is called n times.

int sumVector(int A[], int n){
   if (n == 1)
      return A[0];
   else
      return A[n-1] + sumVector(A,n-1);
}

It may like it uses tail recursion, because the recursive call is technically the last thing performed in the program, however the value returned from the recursive call sumVector(A,n-1) is used by sumVector(A,n), so the call to sumVector(A,n-1) is not the last action performed. If the function is rewritten, in a tail-recursive manner, it does not have to wait for a series of recursive calls to terminate.

int sumVector(int B[], int n, int sum){
   if (n == 0)
      return sum;
   else
      sumVector(B,n-1,sum+B[n-1]);
}

The function now includes a third argument, sum, which is used to accumulate the sum. So instead of the calling function waiting for the value of the recursive call, it passes the accumulating sum to the recursive call. For example in the first instance sumVector(A,8,0) would call sumVector(A,7,0+8) and then terminate. Next sumVector(A,7,8) would call sumVector(A,6,8+7) and terminate, etc. It would be called in the following manner:

int A[100], n=8, sum;
sum = sumVector(A,n,0);

Figure 1 illustrates this in a visual manner. Each line represents the stack at a certain point in the process of performing the recursion. The [ ]’s represent the stack frames. The non-tail recursive version of the function (on the left) uses a maximum of n=8 stack frames to perform the computation, of space complexity O(n). The tail recursive version of the function on the other hand uses constant space in the stack, O(1).

Fig.1: Stack frames for the non-tail recursive and the tail recursive versions.

Note that tail recursion implies the last executed is a recursive call, not necessarily that the recursive call is the last statement appearing in the subprogram. For example tail recursion may appear within a clause of a case-like statement of an if statement where other program lines appear later.

It is often the sort of recursion which can be optimized by compilers such as C or C++.

Thresholding algorithms: Bernsen (local)

This localized method specifies a dynamic threshold for each pixel based on the minimum and maximum intensities in the neighbourhood surrounding the pixel.

Algorithm

The threshold is derived by calculating the minimum and maximum values in a w×w neighbourhood. The threshold value is then simply calculated as the average of these two values.

t = (minN + maxN) / 2

Parameters:

  • minN = minimum intensity in the neighbourhood
  • maxN = maximum intensity in the neighbourhood

The value is used as long as the local contrast is above a predefined limit, cmin. Local contrast is defined as lc=maxN-minN. If lc < c, then the pixel is assumed to belong to a single class and are by default set to the background.

Implementation (in Julia)

Here is the algorithm implement in Julia. Note that because this algorithm processes the neighbourhood surrounding a pixel, the image will need to be padded before the algorithm is applied (preferably with zeros) to allow edge pixels to be processed. Works on 8-bit grayscale images.

function bernsen(img, cmin=15, n=15, bg="bright")

   dx,dy = size(img)
   imgSize = dx * dy
   imgN = copy(img)

   if (bg == "bright")
      K = 0
   elseif (bg == "dark")
      K = 255
   end

   # Calculate the radius of the neighbourhood
   w = div(n,2)

   # Process the image
   for i=w+1:dx-w, j=w+1:dy-w
      # Extract the neighbourhood area
      block = img[i-w:i+w,j-w:j+w]

      # Calculate the min and max of the neighbourhood region
      Zlow = minimum(block)
      Zhigh = maximum(block)
      # Calculate threshold value
      bT = (Zlow + Zhigh)/2.0
      # Calculate local contrast measure
      cl = Zhigh - Zlow

      # If contrast < L, then whole neighborhood is one class
      if (cl < cmin)
         wBTH = K
      else
         wBTH = bT
      end

      # Threshold the pixel
      if (img[i,j] < wBTH)
         imgN[i,j] = 0
      else
         imgN[i,j] = 255
      end
   end

   return imgN
end

Example Use

imgB = bernsen(img,15,15,"bright")

Testing

The algorithm is illustrated using the two images of text shown below. For both algorithms, the default values are used, and the size of the neighbourhood is w=15, and cmin=15, and background (bg) = “bright”.

Example 1
Example 2

The results are somewhat mediocre, and could be improved by tweaking the parameters. The poor performance in Example 2 is likely associated with the disparities in the background of the image, and even the small fluctuations in Example 1 cause issues.

Threshold of Example 1
Threshold of Example 2

Caveats

n/a

REFS:

  1. Bernsen, J., “Dynamic thresholding of grey-level images”, in Int. Conf. on Pattern Recognition, pp.1251-1255 (1986).

Programming made easy (viii) – carbon dating

So far we have covered the basics of programming. Yes, there are more complex programming structures. Programs can be segmented into various subprograms (often called procedures or functions), to provide functionality for algorithms that get used often. This is similar to the math functions that most languages provide, e.g. sqrt(), log() etc. Using a function is easier than having to implement an algorithm for sqrt() every time you want to use it. There are also advanced data structures able to store more complex things, e.g. arrays which hold multiple items of the same type. But simple programs are a good place to start. Let’s consider another example – a program to perform carbon dating.

Radiometric dating is a technique used to date materials based on a knowledge of the decay rates of naturally occurring isotopes and the current abundances. It is our principal source of information about the age of the Earth and a significant source of information about rates of evolutionary change. While the moment in time at which a particular nucleus decays is random, a collection of atoms of a radioactive nuclide decays exponentially at a rate described by a parameter known as the half-life, usually given in units of years when discussing dating techniques. After one half-life has elapsed, one half of the atoms of the substance in question will have decayed. Many of these radioactive substances eventually decay to a final, stable decay product. For example, Uranium-235 decays to Lead-207.

Now let’s specifically look at carbon dating, which is based on the decay of Carbon-14, or C14. An organism acquires carbon from carbon dioxide throughout its lifetime. Plants acquire it through photosynthesis, while animals acquire it through the eating of other animals and plants. The ratio of normal carbon (Carbon-12) to Carbon-14 in the air and in all living things at any given time is nearly constant. When an organism dies, it stops acquiring C14 and the existing isotope decays. The C14 decays with its half-life of 5,730 years, while the amount of C12 remains constant in the sample. By looking at the ratio of C12 to C14 in the sample and comparing it to the ratio in a living organism, it is possible to determine the age of a formerly living thing fairly precisely. The process of carbon dating was discovered by Willard Frank Libby and his colleagues in 1949.

The formulas for radioactive decay is:

t = -(1/λ)ln(Q/100)                    (1)
λ = log(2)/h                           (2)

Where t is the age of the specimen, Q is the amount of C14 remaining, λ is the decay constant of C14 per year, and h is the half-life of the substance (h=5730 years for C14). For example a specimen with 10% C14 remaining will be 19,034 years old.

The algorithm for calculating the age of a specimen is therefore:

  1. Obtain input for percentage of the element remaining (0–100).
  2. Calculate the decay constant using Eq.(2).
  3. Calculate the age of the specimen using Eq.(1).
  4. Output the age of the specimen.

This is what the program looks like in Fortran:

program carbondating
   real :: decayC,  remain, age, halflife
   halflife = 5730.0
   decayC = log(2.0) / halflife
   write(*,*) 'Percentage of the C14 remaining: '
   read(*,*) remain
   age = (-1.0/decayC) * log(remain/100.0)
   write(*,*) 'The age of the sample is ', age,' years'
end program carbondating

Here is what is happening on each line of the program:

  1. Start the program.
  2. Create the variables to hold the values for the equations.
  3. Assign the variable halflife the value 5730.0 for C14.
  4. Calculate the decay constant, decayC, using Eq.(2).
  5. Prompt the user for the percentage of C14 remaining.
  6. Read in the value for the percentage remaining and store it in the variable remain.
  7. Calculate the age of the specimen using Eq.(1).
  8. Output the calculated value.
  9. End the program.

This is a fairly simple program which does simple things, but it could be improved upon. The amount of C14 remaining in the specimen should be > 0 and < 100 (obviously if there is zero percent, there is nothing remaining, and at 100% the specimen is still alive). This just means a small tweak to the program, adding an if-else statement to control how the program deals with values outside the allowed bounds.

program carbondating
   real :: decayC,  remain, age, halflife
   halflife = 5730.0
   decayC = log(2.0) / halflife
   write(*,*) 'Percentage of the C14 remaining: '
   read(*,*) remain
   if (remain > 0.0 .and. remain < 100.0) then
      age = (-1.0/decayC) * log(remain/100.0)
      write(*,*) 'The age of the sample is ', age,' years'
   else
      write(*,*) 'Percent remaining should be >0% and <100%'
   end if
end program carbondating

Here the calculation is only performed if the value of remain entered by the user is valid, otherwise an error message is produced for the user. Here is a sample of the output:

 Percentage of the C14 remaining:
27
 The age of the sample is    10823.7910      years

Is “for” the best term for a loop?

The use of the term for to designate counting loops likely evolved from the use of für in Superplan. It has caught on in most programming languages, but is it the most appropriate term for a loop? The problem with the widely adapted for is that it is just not expressive. Consider the use of for in the following C statement:

for (x=1; x<=1000; x=x+1){
   ...
}

What it does is imply that x has the values 1→1000, but it doesn’t really suggest something will be done. That this is implied by including a statement after the fact is fine, but it isn’t as obvious. The use of for in a loop really implies that it is “used as a function word to indicate an actual or implied enumeration or selection“. It should really be used with the term do, to imply something is being implemented. Likely the design of C assumed this, and just truncated the word do, yet this lessens the readability of the loop.

The first decisive use of for as a loop qualifier likely came in ALGOL-60.

for x := 1 step 1 until 1000 do
begin
   ...
end

However the loop still uses the verb do to imply something will be done. Using for by itself does not really imply a loop is to occur, at least from the perspective of descriptive terms. It is this reason that novice programmers learning C have difficulty with loops (well that and the obscure nature of the for loop statement in C). Consider instead the Fortran version of the loop:

do x = 1,1000
   ...
end do

The term do does imply that something will be done. The “x=1,1000” just assigns constraints on the loop variable.

Maybe a better term would be one that suggests repetition is to be performed: loop (Ada) , or repeat (Pascal) perhaps? In Ada the for loop setup certainly begins with for, but ends with the keyword loop, and the loop itself is terminated with the phrase end loop. This is the same for the while loop, and the generic loop. Here is the sample for loop in Ada:

for x in 1 .. 1000 loop
   ...
end loop

An even better idea would be to create a structure which indicates it is a loop directly, with differing constraints. For example the term loop used by Ada is a good starting point. From a computing point-of-view a loop implies repetition.

loop with x=1..1000
   ...
end loop

The use of the preposition with helps to express the spatial relationship of the variable controlling the loop. It is also easy to extend this one piece of syntax to a family of loops. For example:

loop while x<1000
   ...
end loop

loop until x < 0
   ...
end loop

The choices made for control structures can have a great impact on the learnability of a language. The use of the term do as in Fortran, implies something is to be done.

Programming made easy (vii) – looping

The final piece of the puzzle from an algorithmic viewpoint is repetition. If we wanted to calculate compound interest, or exponential growth a number of times, it would be good to be able to do that from within the program, rather than having to run the program numerous times. Repeating things in a program is done using loops. Programming languages provide various kinds of loops for different circumstances, including for loops when you know how many times something is going to repeat, and while loops for when you don’t.

For example maybe we want to print out the squares and square roots of a series of numbers. Here is a Fortran program which uses a do loop to achieve this:

program sqloop
   integer :: x
   real :: sqrx, sqrtx
   write(*,*) '     Number','       Square','             SQRT'
   do x = 1, 10
      sqrx = x**2
      sqrtx = sqrt(real(x))
      write(*,*) x, sqrx, sqrtx
   end do
end program sqloop

The do loop first says that x will have the values 1 through 10. Not at the same time of course, but x basically controls the loop. We know that because x has the values 1 to 10, then the loop loops 10 times. Each time the loop repeats, it has a new value for x. For example the first loop, x=1, the second loop x=2, etc. After the final loop, when x=10, the do loop ends. Everything between the Line 5 and 9 is performed every time the loop loops. It basically prints a table of the form:

      Number       Square             SQRT
           1   1.00000000       1.00000000
           2   4.00000000       1.41421354
           3   9.00000000       1.73205078
           4   16.0000000       2.00000000
           5   25.0000000       2.23606801
           6   36.0000000       2.44948983
           7   49.0000000       2.64575124
           8   64.0000000       2.82842708
           9   81.0000000       3.00000000
          10   100.000000       3.16227770

Here’s another program with a loop, derived from a popular nursery rhyme, “Ten green bottles”, except that it uses any number of bottles entered by the user.

program bottles
   integer :: n, x
   write(*,*) 'Number of green bottles? '
   read(*,*) n
   do x = n, 1, -1
      write(*,*) x, ' green bottles hanging on the wall'
      write(*,*) x, ' green bottles hanging on the wall'
      write(*,*) 'And if one green bottle should accidently fall,'
      if (x > 2) then
         write(*,*) 'There''ll be ',x-1,' green bottles hanging on the wall.'
      else if (x == 2) then
         write(*,*) 'There''ll be ',x-1,' green bottle hanging on the wall.'
      else
         write(*,*) 'There''ll be no more bottles hanging on the wall.'
      end if
      write(*,*)
   end do
end program bottles

Here the loop goes backwards from n to 1, with a step of -1, so every time the loop loops, the value of x decreases by 1. The if statement inside the loop (Lines 9-15) deals with different lines in the verse, depending on whether there are ≥2, 1, or 0 bottles left. Here is the output for 20 bottles (somewhat abbreviated):

 Number of green bottles?
20
          20  green bottles hanging on the wall
          20  green bottles hanging on the wall
 And if one green bottle should accidently fall,
 There'll be           19  green bottles hanging on the wall.

          19  green bottles hanging on the wall
          19  green bottles hanging on the wall
 And if one green bottle should accidently fall,
 There'll be           18  green bottles hanging on the wall.

...

           1  green bottle hanging on the wall
           1  green bottle hanging on the wall
 And if one green bottle should accidently fall,
 There'll be no more bottles hanging on the wall.

Programming made easy (vi) – making decisions

Until now, the small programs we have written work in a linear fashion, i.e. they start at the top, and work through each statement until they reach the end of the program. But what if an algorithm his a fork in the road, i.e. has to make a decision to go one way or another? In this case a decision must be made. All programming languages provide this type of structure, usually in the form of an if statement. If statements allow a statement to be activated only under certain conditions, or provide one of more alternate paths in a program. An if statement just asks if a certain condition is true or false, and acts accordingly.

For example consider the following Fortran program which calculates the square root of a number:

program sqroot
   real :: x, sqrtx
   write(*,*) 'Enter a number: '
   read(*,*) x
   sqrtx = sqrt(x)
   write(*,*) 'sqrt(', x, ') = ', sqrtx
end program sqroot

The program works fine except when a negative number is input, then it returns the result as NaN, short for Not-an-Number. This can cause issues, and a better solution would be to check that x is not negative before the calculation is performed. This can be done by only performing the square-root calculation if the value input by the user is greater-then or equal to zero.

program sqroot
   real :: x, sqrtx
   write(*,*) 'Enter a number: '
   read(*,*) x
   if (x >= 0) then
      sqrtx = sqrt(x)
      write(*,*) 'sqrt(', x, ') = ', sqrtx
   else
      write(*,*) 'Error: Number must be >= 0.'
   end if
end program sqroot

Now the if statement asks “is x >= 0?”. If this statement is true then it performs the square-root calculation and prints out the result. If x is negative, i.e. “is x >= 0?” is false, then it prints out the error message. The same program in Python would look like this:

import math
x = float(input('Enter a number: '))
if (x >= 0):
    sqrtx = math.sqrt(x)
    print('sqrt(',x,') = ',sqrtx)
else:
    print('Error: number must be >= 0')

Note here the first use of indenting in Python – indenting (4 spaces) is used to imply code association, i.e. the code on lines 4 and 5 are indented and are associated with the part of the if statement when x is greater than or equal to 0.

It is possible to use if statements to check a number of conditions. For example in the simple interest program, the principal amount, and time (years) should all be positive numbers, whereas the rate should be between 0.0 and 1.0. The program below adds these checks and balances by using a nested-if statement.

program simpleInterest
   real :: p, r, t, i
   write(*,*) "principal, rate, time:"
   read(*,*) p, r, t
   if (p <= 0) then
      write(*,*) "No negative or zero balances"
   else if (r < 0.0 .or. r > 1.0) then
      write(*,*) "Rates between 0.0 and 1.0"
   else if (t <= 0) then
      write(*,*) "Time must be > 0"
   else
      i = p * r * t
      write(*,*) "Interest = $", i
   end if
end program simpleInterest

It first checks if the principal, p, is less than or equal to 0 (Line 5). If it is, it prints a message, and the program ends. If p is greater than zero, if then checks to see if the rate, r, is less than 0.0 or greater than 1.0, i.e. out of the bounds 0.0…1.0 (Line 7). If it is out of bounds, then a message is printed and the program ends. If p is greater than 0, and r is within bounds, then it checks if the time, t, is less than or equal to zero (Line 9). If it is, it prints a message, and the program ends. If it passes all three checks, i.e. all three inputs are valid, then it hits the final else statement, and performs the interest calculation (Lines 12-13).

This isn’t really that complicated, but it shows how we can start to build more options into the programs we write.

Programming made easy (v) – input and output

Of course programs are useless if they can’t interact with users. They need what is commonly referred to as I/O or Input and Output. In programming languages I/O is normally provided by a series of functions which read information from the keyboard, and write information to the screen. For example consider the previous program which calculated exponential growth in Fortran:

program expgrowth
   real :: A, A0, k, t
   write(*,*) "Input -> initial value, growth rate, time:"
   read(*,*) A0, k, t
   A = A0 * exp(k * t)
   write(*,*) "New population after ", t, "years is ", A
end program expgrowth

The write(*,*) statement on Line 3 sends a prompt to the screen, asking the user to input three values. This is followed by a read(*,*) statement on Line 4 which reads three values in sequence from the keyboard, and stores them in A0, k and t. After the calculations are performed, the new population is output to the screen. Here is a sample run (the bold text is what is typed by the user):

Input -> initial value, growth rate, time:
500 0.1 50
 New population after    50.0000000     years =    74206.5781

Python does things a little differently. Python incorporates a “user prompt”, into a function called input(), which reads the input from the user. In the situations below, the user input for each value is also converted to a floating-point number using the function float()… this is because in Python, all input is read as a series of characters (a string), so 500 is read as the digits “5”,”0″,”0″, and float() converts it to the value 500.0.

import math
A0 = float(input('initial population value: '))
k = float(input('growth rate: '))
t = float(input('time: '))
A = A0 * math.exp(k * t)
print('New population after ', t, 'years is ', A)

Instead of Fortran’s write(*,*), Python uses print(). Different languages tend to use different names for input and output functions, yet they all do roughly the same thing. It is important to use I/O to make a program as user friendly as possible. Both the programs above prompt the use for input and provide output… imagine if they didn’t? Here is another example program in Fortran which calculates the area of a circle, given the radius.

program areacircle
   real :: radius, area, pi
   pi = 3.14159
   read(*,*) radius
   area = pi * radius**2
   write(*,*) area
end program areacircle

The problem with this program is that it doesn’t communicate with the user. It waits until the user enters something but presumes they know what to enter. Then it produces output, but again without much context. A more user friendly program will prompt the user for input, and format the output. For example:

program areacircle
   real :: radius, area, pi
   pi = 3.14159
   write(*,*) 'Enter the size of the radius: '
   read(*,*) radius
   area = pi * radius**2
   write(*,*) 'The area is ',area,'units^2'
end program areacircle

Now here is the program running:

 Enter the size of the radius:
10
 The area is    314.158997     units^2

Programming made easy (iv) – arithmetic

The simplest things to do in a program are calculations. For example the calculation of the simple interest. So to carry out calculations you need operators, just like on a calculator. Most programming languages provide the same basic arithmetic operators: +, −, * and /. The first two are fairly obvious, the latter two are multiplication, which uses an asterisk instead of the × symbol, and division, with uses a / instead of ÷. Some also offer things like modulus (typically %), and exponentiation (^, or **). Many language also offer up a series of mathematical functions to perform some of the same operations that a calculator performs, e.g. log, ln, sqrt, ex, etc. Languages like C make also use functions for exponentiation, i.e. pow(). Programming languages also allow the use of parenthesis, ( ) to make equations clearer.

Using these operators and functions it is possible to easily create many types of simple programs to perform calculations. Let’s expand upon the simple interest program, and look at the formula for compound interest:

a = p(1 + r/n)nt

p = principal amount
r  = annual rate of interest (a value between 0.0 and 1.0)
t  = number of years
a = amount of money accumulated after n years, including interest.
n  =  number of times the interest is compounded per year

So this is a little trickier than the simple interest formula but not too much. Here it is converted to a equation statement in both Fortran, and Python:

a = p * (1 + r / n) ** (n * t)

It is easy to modify either existing Fortran or Python program to calculate the compound interest, the only thing that has to change is that an additional input is required (n), and the output has to modified. Here is the Fortran program:

program compound
   real :: p, r, t, a, n
   write(*,*) "principal, rate, time, compounds per year:"
   read(*,*) p, r, t, n
   a = p * (1 + r / n) ** (n * t)
   write(*,*) "Amount = $", a
end program compound

If we run the program with p=2000, r=0.032 (3.2%), t=5 (years), and n=4, the answer calculated is: $2345.52881. It is possible to convert any mathematical equation into a statement in a program. Consider the example of exponential growth (or decay).

The mathematical definition says that a quantity that increases with a rate proportional to its current size will grow (or decay) exponentially. Here is the formula:

where:

A = the amount after growth or decay
A0 = the initial value of the population
e = exponential, e = 2.71828183
k = continuous growth rate (k > 0 = growth, k < 0 = decay)
t = the time that has passed

So translating this equation to Fortran/Python we get:

A = A0 * exp(k * t)

So a population with an initial size of 1000, growing at 3% (0.03) per year after 100 years will have a size of 20085.53. Here is the corresponding Fortran program:

program expgrowth
   real :: A, A0, k, t
   write(*,*) "initial value, growth rate, time:"
   read(*,*) A0, k, t
   A = A0 * exp(k * t)
   write(*,*) "New population after ", t, "years is ", A
end program expgrowth

Again, there isn’t much difference between the compound interest program and this program, which suggests that converting programs to a new task isn’t that challenging.

Programming made easy (iii) – data

Programs are based on manipulating, analyzing and interpreting data in order to turn it into information. The tree identification app interprets the data – leaf arrangement, shape, colour, leaf margins, venation patterns etc. to provide the information, i.e. what exact type of tree it is. Data has to be stored in programs in a manner that allows it to be interpreted by a programming language. There are a number of different types of data which can be used in most programming languages, but the three most basic ones are:

  • integers – whole numbers, either positive or negative.
  • real numbers – sometimes called floating-point numbers, which contain a decimal place.
  • characters – any valid alphanumeric data.

For example the year 1967 is stored as an integer, because it is a whole number. The value for π, 3.14159 is a real number. There are often different types of integers and reals in programming languages to deal with different sized numbers. For example 1967 is a very small number and can be stored in much less space than say 2147483647 which needs a lot more space. Space is important because when the program runs the space used equates to memory being used. That’s why in some programming languages you will see an 16, 32, and 64 bit integers – each stores different sized integers. A 64-bit integer can hold values up to 9,223,372,036,854,775,807, which is kind-of overkill if all you want to store is the number 1967.

Storing data in programs means that we need a container, which is called a variable. It normally has a datatype associated with it, which defines what can be stored in the variable, and a name. Some languages like Fortran require the variable to be explicitly defined, others like Python just allow the data to be used and assign the most appropriate datatype.

For example in the simple interest program, there are four pieces of data : the principal amount, the interest rate, the time, and the calculated interest. To store them, the most appropriate datatype is real numbers, because any of them could contain a decimal.

  • principal amount – a positive, real number.
  • interest rate – a positive number between 0.0 and 1.0, representing a percentage, X/100, i.e. if the interest rate is 3%, then the value will be 0.03.
  • time – a positive value representing the number of years.
  • interest – the value calculated.

In Fortran this requires defining the variables, as in the example shown below:

program simple
   real :: p, r, t, i
   p = 1000.0
   r = 0.1
   t = 3.0
   i = p * r * t
   write(*,*) "Interest = $", i
end program simple

The code on Line 2 declares p, r, t and i as variables of type real, and relating to the principal, interest rate, time, and interest respectively. Of course this does not mean that the variables have values – they have values assigned to them in Lines 3-5 of the program. Line 3 means that the variable p, which can store a real number has the value 1000.0 associated with it.

Python does things a little differently. It does not require a formal definition, but rather assigns a datatype to a variable based on what it perceives it to be. So p, r, t and i are all assigned floating point datatypes based on the values assigned to them.

p = 1000.0
r = 0.1
t = 3.0
i = p * r * t
print('Interest = $',i)

Of course the name of the variable is important because an inappropriate choice can make a program less readable. For example p, r, t and i are simply variables, but are not very readable. The use of r to denote interest rate is okay, but it is not at all descriptive – for the human that is, the computer could not care less – however the computer does not have to decipher the program. A better name would be rate. Below is the Fortran program re-written with better variable names.

program simple
   real :: principal, rate, time, interest
   principal = 1000.0
   rate = 0.1
   time = 3.0
   interest = principal * rate * time
   write(*,*) "Interest = $", interest
end program simple

Now a person viewing this program can very quickly see what it does, even though it does not contain any comments (comments are descriptive things added to a program for the sake of humans reading the program, the computer ignores them).