Computing Maths Notes
Computing Maths Notes
Dr Jon Shiach
Spring 2022
Cout
Cin
A
Preliminaries iii
0.1 Learning and Teaching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
0.2 Assessment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
0.3 Advice to students . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
1 Mathematical Fundamentals 1
1.1 Binary numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Hexadecimal numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Tutorial exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2 Logic Circuits 11
2.1 Logic gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2 Circuit diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3 Boolean algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4 Simplifying circuits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5 Universal gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6 Canonical normal form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.7 Karnaugh maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.8 Half and full adders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.9 Tutorial exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3 Algorithms 37
3.1 Greatest common divisor algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.2 Sorting algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.3 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.4 Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.5 Tutorial Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4 Graph Theory 53
4.1 Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.2 Depth-first and breadth-first search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3 Shortest path problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.4 Dijkstra’s algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.5 The Bellman-Ford algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.6 The A* algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.7 Applications of shortest path problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.8 Tutorial exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
A Exercise solutions 89
A.1 Mathematics Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
A.2 Logic Circuits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
A.3 Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
i
Contents Back to Table of Contents
Index 101
iii
Chapter 0. Preliminaries Back to Table of Contents
4 04/04/2022
Easter Break
5 25/04/2021
6 02/05/2021 Revision.
Coursework assignment deadline – 6th May 2022
7 02/05/2022 Assessment week
Examination – 9th May to 10th May 2022
0.2 Assessment
The assessment for this unit takes the form of two coursework assignments and a take home examination.
• Coursework (20%) – Computing Mathematics
– Released to students on Monday 21st March 2022;
– Deadline is 9pm Friday 6th April 2022;
• Coursework (20%) – Computer Graphics
– Released to students on Monday 21st March 2022;
– Deadline is 9pm Friday 6th May 2022;
• Examination (60%)
– Released to students at 09:00 on Tuesday 10th May 2022;
– Deadline is 12:30 on 9pm Friday 6th May 2022;
– Students will have access to the lecture notes, unit materials on moodle and any other sources
of information available;
– Total of 4 questions, 2 questions on computer graphics and 2 questions on computing mathe-
matics.
You will receive a percentage mark for each assessment component and your overall mark for the unit will
be calculated using the simple equation
unit mark = 0.2 × coursework 1 mark + 0.2 × coursework 2 mark + 0.6 × examination mark.
To successfully pass the unit you will need a unit mark of at least 40%.
Mathematical Fundamentals
This chapter will provide a brief explanation of the fundamental mathematical concepts required for students
to understand the material in the subsequent chapters.
or alternatively
Since the value of each digit is the base 10 raised to the power of n where n is the number of digits from
the digit on the far right-hand side then decimal numbers are said to be expressed using base-10.
Since binary numbers are expressed using base-2 then the binary number 101102 has the value
101102 = (1 × 24 ) + (0 × 23 ) + (1 × 22 ) + (1 × 21 ) + (0 × 20 )
= (1 × 16) + (0 × 8) + (1 × 4) + (1 × 2) + (0 × 1)
= 16 + 0 + 4 + 2 + 0
= 22.
Example 1.1
Solution:
(a) 112 = 2 + 1 = 3;
(b) 10112 = 4 + 2 + 1 = 7;
(c) 110002 = 8 + 4 = 12;
(d) 1010111012 = 256 + 64 + 16 + 8 + 4 + 1 = 349.
1
To avoid ambiguity the base of the number is expressed as a subscript.
1
Chapter 1. Mathematical Fundamentals Back to Table of Contents
• repeat the previous step until the result of the division is 0, the digits of the binary number are the
remainders in reverse order.
22 ÷ 2 = 11 remainder 0,
11 ÷ 2 = 5 remainder 1,
5 ÷ 2 = 2 remainder 1,
2 ÷ 2 = 1 remainder 0,
1 ÷ 2 = 0 remainder 1,
Example 1.2
Solution:
(a) (b)
(c)
To add two binary numbers we add the last digits together. If the value of the sum is greater than 1
then we use the last digit of the sum and carry a 1 and include it in the sum of the digits to the left.
This continues until all digits and carries have been summed. For example, consider the sum of the binary
numbers 10110 + 1101
1 0 1 1 0
+ 1 1 0 1
carry 1 1 1
sum 1 0 0 0 1 1
Example 1.3
Solution:
(a) (b)
1 0 1 1 0 1 0 1 0
+ 1 1 + 1 1 0 1 1
carry 1 1 1 carry 1 1 1 1
sum 1 0 0 0 sum 1 0 0 0 1 0 1
An obvious drawback of using binary numbers is that the number of digits required to represent a value
is more than when representing the value using decimal numbers. In computing, it is common to use
hexadecimal numbers which are expressed using base-16 . Since we require an additional six symbols to
represent a single digit of a hexadecimal numbers we use the first six uppercase letters in the alphabet as
shown in table 1.1.
Decimal Hexadecimal
10 A
11 B
12 C
13 D
14 E
15 F
The techniques for converting between decimal and hexadecimal and addition of hexadecimal is the same
as those for binary numbers. For example, the hexadecimal number 12AB16 has the decimal value
Example 1.4
Solution:
1.
2.
3.
4 3 E D
+ F 1 2 B
carry 1 1 1
sum 1 3 5 1 8
1.3 Logic
Mathematical logic deals with statements that can be assigned a value of true or false but not both. Such
statements are known as propositions propositions and the logic using propositions is often known as
propositional logic. For example the statement “it is raining” is a proposition as it is either true or false
whereas the statement “is it raining” is not a proposition as the answer may not be true or false.
In mathematical notation a true value is denoted using the symbol ‘1’ and a false value is denoted using
the symbol ‘0’2
In propositional logic we have three logical connectives: conjunction, disjunction and negation.
A logical conjunction (often referred to as logical AND) between two propositions p and q is true
only if both p AND q are true. In symbolic notation the conjunction of p and q is written as p ∧ q.
A logical disjunction (often referred to as logical OR) between two propositions p and q is true if
p is true OR q is true. In symbolic notation the disjunction of p and q is written as p ∨ q.
2
Some authors use T and F to denote true and false respectively.
A logical negation (often referred to as logical NOT) of the proposition p is true if p is false and
false if p is true. In symbolic notation negation of p is written as ¬p.
Note that we assume an order of precedence negation −→ conjunction −→ disjunction, e.g., in the com-
pound proposition ¬p ∧ q ∨ r we evaluate ¬p first then ¬p ∧ q before evaluating the disjunction with
r.
p q p∧q p∨q ¬p ¬q
0 0 0 0 1 1
0 1 0 1 1 0
1 0 0 1 0 1
1 1 1 1 0 0
Table 1.2: A truth table for the logical connectives AND, OR and NOT.
Here the columns for p and q give all of the possible combinations of the values of the propositions. Since
we have 2 propositions there will be a total of 22 = 4 combinations. The order of which the combinations
are written does not matter although it is recommended practice to write them in ascending order of their
binary value for consistency.
p q ¬p ¬q ¬p ∧ ¬q p∨q ¬(p ∨ q)
0 0 1 1 1 0 1
0 1 1 0 0 1 0
1 0 0 1 0 1 0
1 1 0 0 0 1 0
Table 1.3: Truth table for the compound propositions ¬p ∧ ¬q and ¬(p ∨ q)
Note that the columns for ¬p ∧ ¬q and ¬(p ∨ q) are the same so we can conclude that ¬p ∧ ¬q ≡ ¬(p ∨ q).
Example 1.5
Solution:
(a)
p q p∧q p ∨ (p ∧ q)
0 0 0 0
0 1 0 0
1 0 0 1
1 1 1 1
(b)
p q ¬p ¬p ∨ q p ∧ (¬p ∨ q) p∧q
0 0 1 1 0 0
0 1 1 1 0 0
1 0 0 0 0 0
1 1 0 1 1 1
The definition of conjunction and disjunction also give the following results
0 ∧ p ≡ 0,
1 ∨ p ≡ p.
(p ∧ q) ∨ (¬p ∧ q) ∨ (p ∧ ¬q)
≡ (p ∧ q) ∨ (p ∧ ¬q) ∨ (¬p ∧ q) (commutative law)
≡ (p ∧ (q ∨ ¬q)) ∨ (¬p ∧ q) (distributive law)
≡ (p ∧ 1) ∨ (¬p ∧ q) (complement law)
≡ p ∨ (¬p ∨ q) (identity law)
≡ (p ∨ ¬p) ∧ (p ∨ q) (distributive law)
≡ 1 ∧ (p ∨ q) (complement law)
Example 1.6
Use the laws of logic to simplify the following compound propositions starting clearly which law you
are using at each step:
(a) ¬(¬p ∧ ¬q); (b) p ∨ (q ∧ ¬p); (c) ¬((p∨¬q)∨(r∧(p∨¬q))).
Solution:
(a)
(b)
(c)
¬((p ∨ ¬q) ∨ (r ∧ (p ∨ ¬q))) ≡ ¬(p ∨ ¬q) ∧ ¬(r ∧ (p ∨ ¬q)) (De Morgan’s law)
≡ ¬(p ∨ ¬q) ∧ (¬r ∨ ¬(p ∨ ¬q)) (De Morgan’s law)
≡ ¬(p ∨ ¬q) ∧ (¬(p ∨ ¬q) ∨ ¬r) (commutative law)
≡ ¬(p ∨ ¬q) (absorption law)
≡ ¬p ∧ ¬¬q (De Morgan’s law)
≡ ¬p ∧ q (double negation law)
Logic Circuits
Learning outcomes
On successful completion of this chapters students will be able to:
• identify the different logic gates used to construct logic circuits and draw their truth tables;
• draw circuit diagrams of logic circuits and write down the equivalent Boolean expression;
• draw Karnaugh maps for a Boolean expression and use them to produce a Boolean expression in
canonical disjunctive normal form;
• Construct the half adder and full adder circuits and use them to sum two binary numbers.
A A
A A 0 1
1 0
Figure 2.1: Symbolic representation of a NOT gate and its truth table.
11
Chapter 2. Logic Circuits Back to Table of Contents
A B A·B
0 0 0
A
A·B 0 1 0
B
1 0 0
1 1 1
Figure 2.2: Symbolic representation of an AND gate and its truth table.
2.1.3 OR gate
An OR gate implements logical disjunction on two or more inputs. It is represent mathematically using
A + B and is called the sum of A and B. The symbol representing a OR gate is shown in figure 2.3.
A B A+B
0 0 0
A
A+B 0 1 1
B
1 0 1
1 1 1
A B A⊕B
0 0 0
A
A⊕B 0 1 1
B
1 0 1
1 1 0
Figure 2.4: Symbolic representation of an XOR gate and its truth table.
A B A·B
0 0 1
A
A·B 0 1 1
B
1 0 1
1 1 0
A B A+B
0 0 1
A
A+B 0 1 0
B
1 0 0
1 1 0
f (A, B)
Consider the circuit diagram shown in figure 2.7. Here we have two inputs, A and B, and the circuit
produces a single output f (A, B). The circuit consists of three logic gates: an AND gate, an XOR gate
and an OR gate. The AND and XOR gates both has two inputs A and B, and the outputs from these
gates are then inputted into the OR gate.
Note that the wire from input A is split before the AND gate so A is inputted into both the AND and the
XOR gate (and similar for the input B). In the circuit diagram the A and B wires cross but since there is
no dot we assume that they do not intersect.
The mathematical expression that is equivalent to the circuit in figure 2.7 is
f (A, B) = A · B + A ⊕ B.
Example 2.1
For each circuit diagram below write down a mathematical expression that is equivalent to f (A, B):
1.
f (A, B)
B
2.
A
B f (A, B)
Solution:
1.
2.
When working with logic gates we can simplify circuits by utilising Boolean algebra. Boolean algebra
involves variables that can only take the values of 1 and 0 (or true and false) and thus shares similarities
with propositional logic. The laws of Boolean algebra are given in theorem 2.1 (which are essentially the
same as those for propositional logic) and are used to find expressions that have a value that are equivalent
with the purpose of either simplifying the original expression or using particular logic gates.
Not that we assume and order of precedence where conjunction takes precedence over disjunction, e.g., in
A + B · C the conjunction B · C is evaluated before the disjunction of the result with A is evaluated.
We can also make use of the following which come from the definition of AND and OR
0 · A ≡ 0,
1 + A ≡ 1.
f (A, B)
For example, consider the logic circuit shown in figure 2.8. This circuit consists of 5 logic gates: 2 NOT
gates, 2 AND gates and an OR gate. Writing this in Boolean algebra we have
f (A, B) = A · B + A · B.
The truth table for this circuit is shown in table 2.1 which shows that this circuit produces the same output
as an XOR gate, i.e.,
A · B + A · B ≡ A ⊕ B.
If we want to build an XOR gate using the fewest logic gates then we can apply the laws of Boolean algebra
to simplify A · B + A · B. At first glance it isn’t immediately obvious how we can simplify this expression,
however if we let C ≡ A · B, D ≡ A and E ≡ B then the Boolean expression is
A·B+A·B ≡C +D·E
(C + D) · (C + E) ≡ (A · B + A) · (A · B + B)
so
A · B + A · B ≡ (A · B + A) · (A · B + B).
Using the distributivity law again for each of the two bracketed terms
(A · B + A) · (A · B + B) ≡ (A + A) · (A + B) · (A + B) · (B + B)
(A + A) · (A + B) · (A + B) · (B + B) ≡ 1 · (A + B) · (A + B) · 1
1 · (A + B) · (A + B) · 1 ≡ (A + B) · (A + B).
(A + B) · (A + B) ≡ (A + B) · A · B.
The term A · B is equivalent to a NAND gate so we now have a circuit with 3 logic gates: an OR gate,
a NAND gate and an AND gate. The circuit diagram is shown in figure 2.9 and the truth table for the
circuit is shown in table 2.2 showing that the output is equivalent to that of an XOR gate.
A⊕B
Figure 2.9: A simplified logic circuit for and XOR gate using 3 logic gates.
the number of logic gates as a measure of the propagation delay. Since there are multiple routes from the
inputs to the outputs of a circuit we use the route with the most logic gates for the propagation delay.
Consider the circuit diagram shown in figure 2.7 with the routes from the inputs to the outputs traced in
figure 2.10. Two of the routes pass through 2 logic gates and two of the routes pass through 3 logic gates
so this circuit has a propagation delay of 3.
A
2
3
f (A, B)
3
2
B
Figure 2.10: Tracing the propagation delay for the inputs to the outputs.
Example 2.2
C f (A, B, C)
B
2.
A
B f (A, B, C)
Solution:
1. (a) f (A, B, C) = A · B · A · C + A · B · C
(b)
f (A, B, C) = A · B · A · C + A · B · C
≡ A · B · (A + C) + A · B · C (De Morgan’s law)
≡ A · B · (A + C) + A · B · C (double negation)
≡A·A·B+A·B·C +A·B·C (distributivity law)
≡A·B+A·B·C +A·B·C (idempotence law)
≡ A · B + A · C · (B + B) (distributivity law)
≡A·B+A·C ·1 (complement law)
≡A·B+A·C (identity law)
≡ A · (B + C) (distributivity law)
(c)
A
A · (B + C)
B
C
(d)
A B C B B+C A · (B + C)
0 0 0 1 1 0
0 0 1 1 1 0
0 1 0 0 0 0
0 1 1 0 1 0
1 0 0 1 1 1
1 0 1 1 1 1
1 1 0 0 0 0
1 1 1 0 1 1
2. (a) f (A, B, C) = A · B · C + A · B · C + A · B · C
(b)
f (A, B, C) = A · B · C + A · B · C + A · B · C
≡A·B·C +A·B·C +A·B·C +A·B·C (idempotence law)
≡ A · C · (B + B) + A · B · (C + C) (distributivity law)
≡A·C ·1+A·B·1 (complement law)
≡A·C +A·B (identity law)
≡ A · (B + C) (distributivity law)
(c)
A A · (B + C)
B
C
(d)
A B C B+C A · (B + C)
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 1 0
1 0 0 0 0
1 0 1 1 1
1 1 0 1 1
1 1 1 1 1
A A equivalent to A A
Proof.
• AND gate: A · B ≡ A · B · A · B
A equivalent to A
A·B A·B
B B
Proof.
• OR gate: A + B ≡ A · A · B · B
A
A
A + B equivalent to A+B
B
B
Proof.
• XOR gate: A ⊕ B ≡ A · A · B · B · A · B
A
equivalent A
A⊕B A⊕B
to B
B
Proof.
• NOR gate: A + B ≡ A · A · B · B · A · A · B · B
A
equivalent A
A+B A+B
to B
B
Proof.
A similar approach can be used to prove the NOR gate is also a universal gate.
f (A, B, C) = A · B · C + A · B · C + A · B · C + A · B · C, (2.1)
is in SOP form.
Alternatively we can represent an expression in Canonical Conjunctive Normal Form (CCNF) where a
product of terms where each term is a sum of the inputs. So CCNF is also known as Product Of Sums
(POS) form. For example, the expression
is in POS form.
2.6.1 Minterms
A minterm is a product of the inputs with the condition that each input appears once, either in its
uncomplemented or complemented form. A Boolean expression in SOP form is a sum of minterms, i.e.,
f (A1 , A2 , . . . , An ) = m1 + m2 + · · · + mp ,
where mi are the minterms and A1 , A2 , . . . , An are inputs. For example, the Boolean expression in equa-
tion (2.1) has four minterms A · B · C, A · B · C, A · B · C and A · B · C. Minterms can also be thought
of as the product of the inputs where the truth table for f (A1 , A2 , . . . , An ) has a value of 1.
We can use minterms to represent a Boolean expression in a compact form. We determine a minterm
index, i, for the minterm mi = A1 · A2 · . . . · An which is a decimal number by assigning a value of 1 for the
uncomplemented input A or 0 for the complemented input 1. The values in a minterm are concatenated
to give a binary number which is converted to a decimal which is the minterm index. For example, for the
minterm A · B · C we have
A · B · C ≡ m011 ≡ m3 .
where p is the number of minterms and the summation operator represents a sequence of disjunction
P
operations.
For example, for the Boolean expression in equation (2.1)
f (A, B, C) = A · B · C + A · B · C + A · B · C + A · B · C
≡ m111 + m011 + m101 + m010
≡ m7 + m3 + m5 + m2 ,
so f (A, B, C) = mi .
X
i=2,3,5,7
2.6.2 Maxterms
A maxterm is similar to a minterm but for expressions in canonical conjunctive normal form, i.e., in the
expression
f (A1 , A2 , . . . , An ) = M1 · M2 · . . . · Mp ,
Mi are the maxterms. For example, the expression in equation (2.2) has the four maxterms A + B + C,
A + B + C, A + B + C and A + B + C. Maxterms can also be thought of as the sum of the inputs where
the truth table for f (A1 , A2 , . . . , An ) has a value of 0.
Similar to minterms, we can assign an index, i, for the maxterm Mi = A1 + A2 + · · · + An . For example,
for the maxterm A + B + C we have
A + B + C ≡ M101 ≡ M5 .
where p is the number of maxterms and the product operator represents a sequence of conjunction
Q
operations.
For example, for the Boolean expression in equation (2.2)
f (A, B, C) = (A + B + C) · (A + B + C) · (A + B + C) · (A + B + C)
≡ M100 · M001 · M101 · M110
≡ M 4 · M1 · M5 · M6 ,
so f (A, B, C) ≡ Mi .
Y
i=1,4,5,6
A · B · C + A · B + A + C ≡ A · B · C + A · B · (C + C) + A · C · (B + B)
≡A·B·C +A·B·C +A·B·C +A·B·C +A·B·C
≡A·B·C +A·B·C +A·B·C +A·B·C
≡ m110 + m111 + m011 + m001 ,
X
≡ mi .
i=1,3,6,7
A B C f (A, B, C)
0 0 0 0
0 0 1 0
0 1 0 1
0 1 1 1
1 0 0 0
1 0 1 1
1 1 0 0
1 1 1 1
The minterms are the product of the inputs where f (A, B, C) = 1 and the maxterms are the sum of the
inputs where f (A, B, C) = 0. So to find an expression in POS form that is equivalent to f (A, B, C) we
find the maxterms and invert each input.
To convert from SOP to POS:
1. find the minterms, mi ;
2. identify the maxterm indices by removing the minterm indices from {0, 1, . . . , 2n − 1};
3. convert maxterm indices to binary, find the complement and convert to decimal j;
4. change the to and mi to Mj ;
P Q
f (A, B, C) = A · B · C + A · B · C + A · B · C + A · B · C.
Since there are 3 inputs so the set of all possible minterms is {0, 1, 2, 3, 4, 5, 6, 7} so the maxterms are M0 ,
M1 , M4 and M6 .
To convert from POS to SOP we use the same steps for conversion between SOP to POS with the exception
that we change to . For example, consider following expression in POS form
Q P
f (A, B, C) = (A + B + C) · (A + B + C) · (A + B + C) · (A + B + C)
Example 2.3
For each Boolean expression below write it in SOP and POS forms.
1. f (A, B) = A · (A + B);
2. f (A, B, C) = (A + C) · (A + B).
Solution:
1.
f (A, B) ≡ A · (A + B)
≡A·A+A·B
≡A·B
f (A, B) ≡ m00 = m0
2.
f (A, B, C) = (A + C) · (A + B)
≡A·A+A·B+A·C +B·C
≡A·B+A·C +B·C
≡ A · B · (C + C) + A · C · (B + B) + B · C · (A + A)
≡A·B·C +A·B·C +A·B·C +A·B·C +A·B·C +A·B·C
≡A·B·C +A·B·C +A·B·C +A·B·C +A·B·C
Consider the truth table of the Boolean expression A + B shown in table 2.4. We can represent this
as a Karnaugh map by forming a grid with the inputs represented by the rows and columns of the grid
A B A+B
0 0 0
0 1 1
1 0 1
1 1 1
(figure 2.11). The outputs of the Boolean expression are represented by 0s and 1s in the Karnaugh map
corresponding to their inputs.
B
A 0 1
0 0 1
1 1 1
B
A 0 1
0 0 1
1 1 1
Taking the vertical column (blue) in figure 2.12 to start with here we see that the the input value A is 1
for both the elements in the group and the input values B is both 0 and 1. What this means is that this
group represents the minterm A since it is alway true.
Now looking at the horizontal row (red) we see that the input value A is both 0 and 1 whilst the input
value B is 1 for both of the 1s in the group. Therefore this group represents the minterm B.
Since each grouping of a Karnaugh map represents a minterm for the Boolean expression it represents then
the expression for this Karnaugh maps is A + B as expected..
Alternatively, groups of 0s in a Karnaugh map corresponds to the maxterms in a Boolean expression which
is the complement of the expression that the Karnaugh map represents f (A1 , A2 , . . . , An ). The Karnaugh
map in figure 2.13 is the Karnaugh map for A+B (figure 2.11) with the 0s grouped. This group corresponds
to the maxterm A · B which is equivalent A + B, i.e., the complement of A + B.
B
A 0 1
0 0 1
1 1 1
• Input numbering can only change by a single bit from one row or column to the next.
correct incorrect
BC BC
00 01 11 10 00 01 10 11
A A
0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 1 1 1 0 0
correct incorrect
BC BC
A 00 01 11 10 A 00 01 11 10
0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 1 1 1 0 0
correct incorrect
BC BC
A 00 01 11 10 A 00 01 11 10
0 0 0 1 0 0 0 0 1 0
1 1 1 0 0 1 1 1 0 0
correct incorrect
BC BC
A 00 01 11 10 A 00 01 11 10
0 0 0 0 0 0 0 0 0 0
1 1 1 1 0 1 1 1 1 0
2 groups of 2 1 group of 3
• Each group should be as large as possible (not doing this will not break the laws of Boolean algebra
but it wouldn’t result in the simplest form).
correct incorrect
BC BC
A 00 01 11 10 A 00 01 11 10
0 1 1 1 1 0 1 1 1 1
1 0 0 1 1 1 0 0 1 1
• Groups may wrap around the edges of the Karnaugh map. The leftmost column may be grouped
with the rightmost column and the top row may be grouped with the bottom row.
correct incorrect
BC BC
A 00 01 11 10 A 00 01 11 10
0 1 0 0 1 0 1 0 0 1
1 1 0 0 1 1 1 0 0 1
1 0 1
1 0 1
The horizontal (blue) group corresponds to the minterm A and the vertical (red) group corresponds to the
minterm B so this Karnaugh map represents A + B. If we simplify the original expression using the laws
of Boolean algebra
A · B + A · B + A · B ≡ A · (B + B) + A · B (distributivity law)
≡A+A·B (complement law)
≡ (A + A) · (A + B) (distributivity law)
≡ A + B. (complement law)
Which shows that the simplification using the Karnaugh map is correct. Note that Karnaugh maps give
the Boolean expression in SOP form which may be simplified further by use of the laws of Boolean algebra.
Example 2.4
Solution:
1. (a)
A B A·B+A·B+A·B
0 0 1
0 1 1
1 0 1
1 1 0
(b)
B
A 0 1
0 1 1
1 1 0
f (A, B) = A + B
(c)
A
B 0 1
0 1 1
1 1 0
f (A, B) ≡ A · B
f (A, B) ≡ A · B
≡A+B
(d)
A B A+B
0 0 1
0 1 1
1 0 1
1 1 0
2. (a)
1 1 0 1 1
f (A, B, C) ≡ B + A · C
(c)
BC
A 00 01 11 10
0 0 0 1 1
1 1 0 1 1
f (A, B, C) ≡ (A · B) + (B · C)
f (A, B, C) ≡ (A · B) · (B · C)
≡ (A · B) + (B · C)
≡ (A + B) · (B + C)
≡ (A + B) · (B + C)
(d)
A B C B+A·C (A + B) · (B + C)
0 0 0 0 0
0 0 1 0 0
0 1 0 1 1
0 1 1 1 1
1 0 0 1 1
1 0 1 0 0
1 1 0 1 1
1 1 1 1 1
0 + 0 = 00,
0 + 1 = 01,
1 + 0 = 01,
1 + 1 = 10.
We refer to the digit on the right of the output as the sum or S and the digit on the left as the carry or
C. The first three cases we only need 1 bit to store the output since the value of the carry is 0 but in
the fourth case where the value of the carry is 1 we will require 2 bits to store the output. So we need to
create a logic circuit that can perform this addition and output the sum and the carry. The truth table for
the addition of two 1-bit numbers is shown in table 2.5.
Table 2.5: Truth table for the addition of two 1 bit numbers.
inputs outputs
A B C S
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 0
The corresponding Karnaugh map for the S and C columns from table 2.5 are shown in figure 2.14.
B B
A 0 1 A 0 1
0 0 0 0 0 1
1 0 1 1 1 0
Figure 2.14: Karnaugh maps for the sum and carry column for the addition of two 1 bit numbers.
S = A · B, (2.3)
C =A·B+A·B
≡ A ⊕ B, (2.4)
which is an XOR gate. Therefore the logic circuit for adding two 1 bit numbers is shown in figure 2.15.
This circuit is known as the half adder.
A
S
C
B
0 1
1 1 +
carry
sum
Starting at with the right column, we have the sum 1 + 1 = 10 so we put 0 in the sum and we carry 1 over
to the next column to the left.
0 1
1 1 +
carry 1
sum 0
Moving to the next column to the left we have the sum 0 + 1 + 1 = 10 so again we put 0 in the sum and
carry the 1 to the next column on the left.
0 1
1 1 +
carry 1 1
sum 0 0
We now have no more columns and the answer can be read as the last carry value along with the sum
which is 100 as required (i.e., 01 + 11 in binary is equivalent to 1 + 3 in decimal which is 4 and the binary
equivalent is 100).
To build circuits that can sum 2 or more bit numbers we need to be above to sum to 1 bit numbers and
a carry from previous sums. This circuit is known as the full adder which takes inputs of two 1 bit values
A and B as well as a carry value Cin which may have come from a previous calculation. The truth table
for a full adder is shown in table 2.6 and the Karnaugh maps for the Cout and S columns are shown in
figure 2.16.
inputs outputs
A B Cin Cout S
0 0 0 0 0
0 0 1 0 1
0 1 0 0 1
0 1 1 1 0
1 0 0 0 1
1 0 1 1 0
1 1 0 1 0
1 1 1 1 1
BCin BCin
A 00 01 11 10 A 00 01 11 10
0 0 0 1 0 0 0 1 0 1
1 0 1 1 1 1 1 0 1 0
Figure 2.16: Karnaugh maps for the sum and carry column for a full adder.
Grouping the 1s in figure 2.16(a) gives the Boolean expression A·B +B ·Cin +A·C which can be simplified
using the distributivity law to
Cout = A · B + Cin · (A + B). (2.5)
Grouping the 1s in figure 2.16(b) (which is trivial as we only have groups of size 1) gives the Boolean
expression A · B · C + A · B · C + A · B · C + A · B · C. Simplifying using the laws of Boolean algebra
S =A·B·C +A·B·C +A·B·C +A·B·C
≡ C · (A · B + A · B) + C · (A · B + A · B) (distributivity law)
≡ C · (A · B + A · B) + C · (A · B + A · B) (see below)
≡ C · (A ⊕ B) + C · (A ⊕ B) (definition of A ⊕ B)
≡ A ⊕ B ⊕ C. (definition of A ⊕ B) (2.6)
It might not immediately be obvious to you that A · B + A · B ≡ A · B + A · B in the third line so let’s
take moment here to check this
A·B+A·B ≡A+B·A+B (De Morgan’s law)
≡ (A + B) · (A + B) (De Morgan’s law)
≡A·A+A·B+A·B+B·B (distributive law)
≡0+A·B+A·B+0 (complement law)
≡ A · B + A · B. (identity law)
So we now have two Boolean expressions for Cout and S in equations (2.5) and (2.6) for a full adder. One
thing to note that the A · B term in equation (2.5) is the carry output from the half adder equation (2.3)
and the A ⊕ B in S is the sum output from the half adder equation (2.4). Therefore the circuit for the full
adder can be built by combining two half adder circuits where the sum output from the first half adder is
combined with Cin in the second half adder where the sum output from the second half adder is S and the
two carry outputs are combined using an XOR gate to give Cout . The circuit for the full adder is shown in
figure 2.17.
Cin
S
A
Cout
Note that we could replace the XOR gate connecting the two carry outputs with an OR gate and not affect
the circuit, however this would mean using three different types of gates (XOR, AND and OR) when for
manufacturing reasons fewer types is often preferable.
A3 B 3 A2 B2 A1 B 1 A0 B 0
S3 S2 S1 S0
Figure 2.18: Four single bit full adders combined to make a ripple adder to compute the sum of
two 4 bit numbers.
Consider the ripple carry adder shown in figure 2.18. This consists of four single bit full adders so it can
perform the addition of two 4 bit binary numbers A3 A2 A1 A0 and B3 B2 B1 B0 . The inputs to full adder 0
are the rightmost digits of the two numbers, A0 and B0 , and 0 (note that full adder 0 could also be a half
adder since the Cin input is 0 but it is simpler just to use all full adders here). The outputs for adder 0 are
the rightmost digit of the sum S0 and the carry C0 . The inputs to adder 1 are the next two digits to the
left, A1 and B1 , and the carry from adder 0. The outputs for adder 1 are the next digit to the left of the
sum S1 and the carry C1 . This pattern continues along the chain until all bits have been added together,
the digits of the final sum are C3 S3 S2 S1 S0 .
1 1 0 0 0 1 1 1
0 1 0 0
Figure 2.19: The addition of A = 1001 and B = 1011 using a ripple carry adder.
2.8.4 Representing the half adder and adder using NAND gates
As mentioned in the previous section, it is preferable to use fewer types of gates in a logic circuit where
possible. Since NAND gates are universal gates we can replace the XOR and AND gates in figures 2.15
and 2.17 with the NAND gates equivalents from section 2.5 to give the circuit diagrams shown in figures 2.20
and 2.21.
Figure 2.20: The half adder constructed using only NAND gates.
Cout
Cin
A
Figure 2.21: The full adder constructed using only NAND gates.
A C
A B C Q
Q
0 0
B 0 1
1 0
1 1
(c)
A C
Q
A B C D Q
B 0 0
D 0 1
1 0
1 1
Exercise 2.2. Draw circuit diagrams for the following Boolean expressions:
(a) A + B · B; (b) (A · B) · (A + C); (c) A · B + C + A · B.
Exercise 2.3. Simplify the following Boolean expressions. For each step of the simplification, state which
laws you have used.
(a) A · (A + B); (b) (A + AB) · (C + B · C);
Algorithms
Learning outcomes
On successful completion of this chapters students will be able to:
• read an algorithm from pseudocode and implement it;
• apply the bubble sort, quicksort and merge sort algorithm to sort a list of numbers into ascending
order;
• understand and apply recursive operations;
• compare the time cost of algorithms using big-O notation.
So GCD(315, 588) = 21 (note that the a ← x notation means a is defined to be equal to x). Here steps
k = 2 to k = 7 involved repeated subtraction of 42 which eventually resulted in a assuming the value of
37
Chapter 3. Algorithms Back to Table of Contents
21. This is the same as dividing 273 by 42 and finding the remainder which is 21, i.e.,
273 = 6 × 42 + 21.
Since a takes on the value of the remainder and b is now larger than a then the next calculation is to set
b to b − a. To ensure we are always doing the same calculation we can set b to the remainder and a to b.
Therefore Euclid’s algorithm can be rewritten so that the values at the k th step are
ak ← bk−1 ,
bk ← mod(ak−1 , bk−1 ),
where the subscript k − 1 denotes the values from the previous step and mod(a, b) is the modulo operator
which returns the remainder of a ÷ b. The algorithm terminates when a = b which will be when mod = 0.
Applying the rewritten Euclid’s algorithm to a = 588 and b = 315
if a < b
t = a;
a = b;
b = t;
end
while b ~= 0
t = b;
b = mod (a , b ) ;
a = t;
end
end
6 1 5 3 7 2 4 1 5 3 6 2 4 7 1 3 5 2 4 6 7 1 3 2 4 5 6 7
1 6 5 3 7 2 4 1 5 3 6 2 4 7 1 3 5 2 4 6 7 1 3 2 4 5 6 7
1 5 6 3 7 2 4 1 3 5 6 2 4 7 1 3 5 2 4 6 7 1 2 3 4 5 6 7
1 5 3 6 7 2 4 1 3 5 6 2 4 7 1 3 2 5 4 6 7
1 5 3 6 7 2 4 1 3 5 2 6 4 7 1 3 2 4 5 6 7
1 5 3 6 2 7 4 1 3 5 2 4 6 7
1 5 3 6 2 4 7 1 3 5 2 4 6 7 1
(a) Pass 1 (b) Pass 2 (c) Pass 3 (d) Pass 4
In the first pass, figure 3.1(a), five swaps were required to move 6 and 7 up the list. In the second pass,
figure 3.1(b), since we know that the largest number in the list must now be at the top we have one fewer
pairs to check. After the second pass we have the highest two numbers in the correct place. The algorithm
requires a further two passes through the list to sort it into the correct order and another pass (not shown)
that would not have any swaps so the algorithm terminates.
Example 3.1
Use the bubble sort algorithm to sort the numbers [3, 1, 6, 5, 4, 2].
Solution:
Pass 1: there are 6 numbers in the list so we need to check 5 pairs in pass 1
[3, 1, 6, 5, 4, 2] swap 3 and 1
[1, 3, 6, 5, 4, 2] do not swap 3 and 6
[1, 3, 6, 5, 4, 2] swap 6 and 5
[1, 3, 5, 6, 4, 2] swap 6 and 4
[1, 3, 5, 4, 6, 2] swap 6 and 2
[1, 3, 5, 4, 2, 6]
Pass 2: need to check 4 pairs
[1, 3, 5, 4, 2, 6] do not swap 1 and 3
[1, 3, 5, 4, 2, 6] do not swap 3 and 5
[1, 3, 5, 4, 2, 6] swap 5 and 4
[1, 3, 4, 5, 2, 6] swap 5 and 2
[1, 3, 4, 2, 5, 6]
Pass 3: need to check 3 pairs
[1, 3, 4, 2, 5, 6] do not swap 1 and 3
[1, 3, 4, 2, 5, 6] do not swap 3 and 4
[1, 3, 4, 2, 5, 6] swap 4 and 2
[1, 3, 2, 4, 5, 6]
Pass 4: need to check 2 pairs
[1, 3, 2, 4, 5, 6] do not swap 1 and 3
[1, 3, 2, 4, 5, 6] swap 3 and 2
[1, 2, 3, 4, 5, 6]
Pass 5: need to check 1 pair
[1, 2, 2, 4, 5, 6] do not swap 1 and 3
[1, 2, 3, 4, 5, 6]
3.2.2 Quicksort
The major disadvantage with the bubble sort algorithm is that it is slow to implement. A quicker algorithm is
quicksort which is a popular sorting algorithm developed by British computer scientist Tony Hoare (1961).
The algorithm works by choosing a number from the list to be the pivot, the list is then partitioned about
the pivot so that those numbers that are less than the pivot number are moved to the left of the pivot and
those numbers that are greater than the pivot are moved to the right of the pivot. This process is repeated
for the two sub-lists which are less than or greater than the pivot. Eventually the sub-lists will contain just
element and the list will be in sorted order.
The choice of the pivot element is arbitrary, in the examples presented here the last element in the list
is chosen for simplicity. The partitioning of the list is done setting an index i to 1 (the index of the first
element in the loop so for a Python implementation this would be 0) and look for an element in the list
that is less than the pivot element. We swap this element with the one with index i and increment i by 1.
This continues until all non-pivot elements have been checked and then we swap the pivot element with
the one with index i which places the pivot between the left and right sub-lists.
The pseudocode for the quicksort algorithm is shown in algorithm 3. The algorithm uses two functions,
quicksort is a function that calls itself recursively starting with l = 1 and r = n (the index of the last
element in the list). The function partition performs that partitioning of the lists about the pivot.
For example, consider the implementation of the quicksort algorithm used to sort the list [6, 1, 5, 3, 7, 2,
4] as shown in figure 3.2. The steps of the algorithm are:
(1) X = [6, 1, 5, 3, 7, 2, 4]: l = 1 and r = 2 then l < r so swap 6 and 2;
(2) X = [2, 1, 5, 3, 7, 6, 4]: l = 3 and r = 4 then l < r so swap 5 and 3;
(3) X = [2, 1, 3, 5, 7, 6, 4]: l = 4 and r = 3 then l > r so swap 5 with the pivot 4;
(4) X = [2, 1, 3, 4, 7, 6, 5]: partitioning is complete for pivot 4, apply quicksort to sub-lists [2, 1, 3] and
[7, 6, 5]
(5) X = [2, 1, 3]: l = 3 and r = 1 then l > r so swap 3 with the pivot (which is itself);
X = [5, 6, 7]: l = r = 7 so swap 7 with the pivot 5;
(6) X = [2, 1, 3]: partitioning is complete for pivot 3, apply quicksort to the sub-list [2, 1];
X = [5, 6, 7]: partitioning is complete for pivot 5, apply quicksort to the sub-list [6, 7];
X = [6, 7]: l = 7 and r = 6 then l > r so swap 7 with the pivot (which is itself);
(8) X = [1, 2]: partitioning is complete for pivot 2, apply quicksort to the sub-list [2];
X = [6, 7]: partitioning is complete for pivot 7, apply quicksort to the sub-list [6];
X = [6]: list of of length 1 so exit and terminate algorithm. The final sorted list is X =
[1, 2, 3, 4, 5, 6, 7].
l r
1 6 1 5 3 7 2 4
l r
2 2 1 5 3 7 6 4
r l
3 2 1 3 5 7 6 4
4 2 1 3 4 7 6 5
r l l, r
5 2 1 3 4 7 6 5
6 2 1 3 4 5 6 7
l, r r l
7 2 1 3 4 5 6 7
8 1 2 3 4 5 6 7
9 1 2 3 4 5 6 7
1 2 3 4 5 6 7
Example 3.2
Solution:
Choosing the last number in the list to be the pivot.
All sub-lists only have one elements so the sorted list is [1, 2, 3, 4, 5, 6].
function quicksort(X, l, r)
if l < r then
X, pivot ← quicksort(X, l, r) . move list elements either side of the pivot
X ← quicksort(X, l, p − 1) . repeat algorithm for left sub-list
X ← quicksort(X, p + 1, r) . repeat algorithm for right sub-list
end if
return X
end function
by taking the next smallest element from A or B. The final merged list will contain the sorted list.
For example, consider the implementation of the merge sort algorithm used to sort the list [6, 1, 5, 3, 7,
2, 4] as shown in figure 3.3. The steps of the algorithm are:
(1) List [6, 1, 5, 3, 7, 2, 4] – the midpoint of the list is 3 the index of which is found using i =
int((listlength + 1)/2). The list is split into two sub-lists [6, 1, 5, 3] (containing the midpoint since
there are an odd number of elements) and [7, 2, 4].
(2) Sub-list [6, 1, 5, 3] – the midpoint is 1 so the list is split into the sub-lists [6, 1] and [5, 3].
(3) Sub-list [6, 1] – the midpoint is 6 so the list is split into the sub-lists [6] and [1].
(4) The sub-lists [6] and [1] are merged into the sub-list [1, 6].
(5) Sub-list [5, 3] – the midpoint is 5 so the list is split into the sub-lists [5] and [3].
(6) The sub-lists [5] and [3] are merged into the sub-list [3, 5].
(7) The sub-lists [1, 6] and [3, 5] are merged into the sub-list [1, 3, 5, 6].
(8) Sub-list [7, 2, 4] – the midpoint is 2 so the list is split into the sub-lists [7, 2] and [4].
(9) Sub-list [7, 2] – the midpoint is 7 so the list is split into the sub-lists [7] and [2].
(10) The sub-lists [7] and [2] are merged into the sub-list [2, 7].
(11) The sub-lists [2, 7] and [4] are merged into the sub-list [2, 4, 7].
(12) the sub-lists [1, 3, 5, 6] and [2, 4, 7] are merged into the sorted list [1, 2, 3, 4, 5, 6, 7].
midpoint
6 1 5 3 7 2 4
1
6 1 5 3 7 2 4
2 8
6 1 5 3 7 2 4
3 5 9
6 1 5 3 7 2
4 6 10
1 6 3 5 2 7
7 11
1 3 5 6 2 4 7
12
1 2 3 4 5 6 7
Example 3.3
Use the merge sort algorithm to sort the numbers [3, 1, 6, 5, 4, 2].
Solution:
Underlined numbers denote the midpoint.
[3, 1, 6, 5, 4, 2] partition list
[ [3, 1, 6], [5, 4, 2] ] partition sub-lists [3, 1, 6], [5, 4, 2]
[[ [3, 1], [6] ], [ [5, 4], [2] ]] partition sub-lists [3, 1] and [5, 4]
[[[ [3], [1] ], [6]], [[ [5], [4] ], [2]]] merge [3] with [1], and [5] with [4]
[[ [1, 3], [6]] , [ [ 4, 5 ], [2] ]] merge [1, 3] with [6] and [4, 5] with [2]
[ [1, 3, 6 ], [2, 4, 5 ] ] merge [1, 3, 6] with [2, 4, 5]
[1, 2, 3, 4, 5, 6]
function mergesort(X)
if length(X) > 1 then
set m ← int((length(X) + 1)/2) . calculate index of midpoint
call L ← mergesort(X(1 : m)) . apply merge sort to left-hand list
call R ← mergesort(X(m + 1 : end)) . apply merge sort to right-hand list
call X ← merge(L, R) . merge left and right-hand lists
end if
return X
end function
3.3 Recursion
The pseudocode for the quicksort and merge sort algorithms shown in algorithms 3 and 4 both involve a
function making a call to itself. This is known as recursion (see section 3.31 ) and is often used in divide
and conquer algorithms.
When using recursion in an algorithm the following structure is used:
• base case – this is a command used when a recursive step is not required and the current scenario
should terminate. For example, in algorithm 4, the mergesort function will return the list X if its
length is equal to 1, i.e., the list cannot be partitioned further.
• recursive step – this is a set of commands that are applied which will eventually result in the base
case being met. For example, in algorithm 4 if the length of the list X is greater than 1 then X is
split into two and the mergesort function is called recursively to each sub-list.
Consider the Fibonacci series which is defined by
0,
if n = 0,
Fn = 1, if n = 1,
n−2 + Fn−1 , ifi > 1.
F
F0 = 0, F1 = 1, F2 = 1, F3 = 2, F4 = 3, F5 = 5, ...
We can write an algorithm for computing the Fibonacci number Fn using recursion is shown in algorithm 6.
The base case is when n < 2 and the algorithm returns Fn = n (i.e.. F0 = 0 or F1 = 1). The recursive
step is implemented when n ≥ 2 and
We can visualise recursion using a tree structure. Consider the Fibonacci number F5 calculated using
recursion as shown in algorithm 6 which is represented using a tree in figure 3.4. Since n ≥ 2 then
F5 = F3 + F4 which also require a recursive step to calculate F3 = F1 + F2 and F4 = F2 + F3 and
so on. Where a base case is encountered we have a leaf node (see chapter 4 for details on trees) where
F0 = 0 and F1 = 1. The values of the parent nodes are the sum of the values of the child nodes, e.g.,
F2 = F0 + F1 = 0 + 1 = 1. Computing the values for all nodes in the tree gives F5 = 5.
1
This is a joke stolen from Google where if you search for ‘recursion’ it returns a message above the search results that
states “Did you mean: recursion” which links to the same page.
F5 = 5
F3 = 2 F4 = 3
F1 = 1 F2 = 1 F2 = 1 F3 = 2
F0 = 1 F1 = 1 F0 = 0 F1 = 1 F1 = 1 F2 = 1
F0 = 0 F1 = 1
Example 3.4
Write pseudocode that uses recursion to compute the sum of the numbers between 1 and n.
Solution:
We need to write a function sum(n) which will return the value 1 + 2 + · · · + n. So if n is the input
we need to go through the numbers in descending order, i.e.,
sum(n) = n + sum(n − 1)
= n + (n − 1) + sum(n − 2)
..
.
= n + (n − 1) + · · · + 2 + sum(1)
So the base case is when n = 1 and we return the value of n and in the recursive step we need
to add the current number n to the sum of all of the numbers less than n, i.e., n + sum(n − 1).
Therefore the pseudocode is
function sum(n)
if n = 1 then
return n
end if
return n + sum(n − 1)
end function
3.4 Complexity
Complexity is the cost of implementing an algorithm and is based on the size of the input n. Even though
different algorithms may achieve the same results, they may have required more steps or computations to
get there. For example, in the previous section we looked three different algorithms for performing the
same action, sorting a list of numbers into ascending order. These algorithms were coded in Python and
used to sort an lists containing 1000 random integers. The times taken to sort the list are shown below.
Listing 3.3: Times taken for sorting algorithms to sort 1000 random numbers.
Bubble sort : 0.3325 s
Quicksort : 0.0552 s
Merge sort : 0.0054 s
So the bubble sort algorithm took the longest, followed by the quicksort algorithm and merge sort algorithm
was the quickest. So different algorithms take different amounts of time to run on a computer. The reason
for this is that they use different number of arithmetic operations and different memory requirements. It
is useful to know which algorithm will perform the quickest when presented with a task.
The factors affecting the speed which an algorithm runs on a computer are:
(i) the speed of the computer hardware;
(ii) the efficiency programming language used;
(iii) the efficiency of the program (i.e., the programming skills of the programmer);
(iv) the hardware requirements of the tasks running in the background (i.e., other programs running at
the same time);
(v) the complexity of the algorithm;
(vi) the size of the input.
Here (i) to (iv) will vary so can be ignored when analysing the speed of an algorithm where we focus on (v)
and (vi). The speed of computer hardware tends to increase over time so we need a way of measuring the
cost of an algorithm that is independent of time. For this reason we count the number of times a “principal
activity” is performed in an algorithm. The principal activity may depend on the type of algorithm, for
example in the bubble sort algorithm we were comparing pairs of numbers whereas in Euclid’s algorithm
we we calculating the modulo between two numbers.
Another thing we need to consider is that an algorithm may require different number of operations for
different inputs, for example, using the quicksort algorithm to sort an list that has already been sorted
would require no swaps so would be performed quicker than when used to sort and unsorted list. So we
think of the worst case, best case and average case
• worst case – this is the maximum number of times the principal activity has been performed for all
inputs of size n;
• best case – the minimum number of times the principal activity is performed for specific inputs of
size n;
• average case – the number of times the principal activity is performed on average.
The average case is the most useful measure as in practice we will rarely have an input that results in the
worst or best cases. However, this is difficult to measure since we are unlikely to know how the input set
will be distributed.
swapping then if we have n numbers in the list on the first pass we have n − 1 pairs to check. At the end
of the first pass we know that the largest number in the list is in the correct position at the end so on the
second pass we only need to check n − 2 pairs. Assuming that each pass requires at least one swap then
This is the sum of an arithmetic series with the first term a = 1 and common difference d = 1 so
n
number of checks = (2a + (n − 1)d)
2
(n − 1)
= (2 + ((n − 1) − 1))
2
1 1
= n2 − n.
2 2
So the cost of the worst case for the bubble sort algorithm is 12 n2 − 12 n. Let t(n) be the time taken for a
computer to apply the bubble sort algorithm, if we have two computers, A and B, where computer B is
10 times faster than computer A then
Note that for both computers, the value of the first term, 107 and 106 , is much larger than the value of
the second term, 104 and 103 so we say that the first term dominates. When talking about the complexity
of an algorithm, we ignore all but the first term, n2 , and make the statement
“t(n) grows like n2 as n increases”
What this means is that if we apply the bubblesort algorithm to sort two lists one twice as long as the
other, then we would expect the larger list to take 22 = 4 times as long as the smaller list. Also, note that
this statement is independent of the speed of different computers.
This concept represented mathematically using big-O notation where if the value of the function t(n)
grows like n2 as n increases then we say
t(n) = O(n2 ).
• O(n!) factorial time: the time taken for each step of an algorithm is proportional to the factorial
of the size of the input n. Take the classic travelling salesman problem where a sales man wants to
find a path that visits a number of cities with the shortest travelling distance (we look at shortest
path problems in chapter 4). If we were to exhaustively check each possible path then for n cities
we have n! permutations, i.e., for 10 cities we have 10! = 3628800 permutations to consider but for
20 cities we have 20! = 2.4 × 1018 permutations (to put this number into perspective if it took 1
millisecond to check each permutation then it would take 77 million years to check them all).
The sorting algorithms studied in this chapter have the following complexities:
• Bubblesort
– worst case O(n2 )
– average case O(n2 )
– best case O(n)
• Quicksort
– worst case O(n2 )
– average case O(n log n)
– best case O(n log n)
• Merge sort
– worst case O(n log n)
– average case O(n log n)
– best case O(n log n)
(b) Write down the steps used by your algorithm to find the minimum value for the list [5, 6, 3, 7, 4].
(c) Code your function in Python or MATLAB and text your code on the list in part (b).
Exercise 3.7. The pseudocode for Gaussian elimination, which solves a system linear equations of the form
Ax = b, is given in algorithm 7.
Graph Theory
Learning outcomes
On successful completion of this chapters students will be able to:
• perform depth-first and breadth-first search to obtain node ordering and a spanning tree;
4.1 Graphs
In mathematics a graph is an ordered pair G = (V, E) where
A graph is represented diagrammatically using circles to represent the nodes and lines to represent the
edges (figure 4.1). The configuration of nodes does not matter as long as the lines that represent the edges
connect the correct nodes.
C D
A B
Two nodes are said to be adjacent (or incident) if they are connected by an edge. For example,
nodes A and D are adjacent to node A in figure 4.1.
53
Chapter 4. Graph Theory Back to Table of Contents
The degree of a node vi , denoted by deg(vi ) is the number of nodes that are adjacent to node vi .
For example, the node D in figure 4.1 has degree deg(D) = 2. In the case where an edge is a loop
that connects a node with itself we add 2 to the degree of the node. The node A in figure 4.1 has
degree deg(A) = 5.
The origins of graph theory can be traced to swiss mathematician Leonard Euler (1707 – 1783) where in
1736 he used it to solve the problem of whether it is possible to walk over the seven bridges of Königsberg
crossing each one only once (now known as an ‘Eulerian path’). To solve this problem Euler created the
first mathematical graph where the map of Königsberg (figure 4.2(b)) is represented as a graph (figure 4.2).
The seven bridges are the edges of the graph which join four nodes which are the land masses that are
separated by the river Pregel where node A is the island in the centre, node B is the north of the city,
node C is the south of the city and node D is the west of the city.
A D
(a) Map of Königsberg (b) Graph for the bridge of Königsberg problem
In his solution, Euler noticed that in order to traverse an edge just once, zero or two nodes of the graph
must have an odd degree. Since all four nodes in figure 4.2 have an odd degree then it is not possible to
create a path that crosses each of the seven bridges just once.
A B C D
A 0 2 2 1
A = B 2 0 0 1.
C 2 0 0 1
D 1 1 1 0
A graph G is said to be connected if a path exists between any two nodes in G. If a graph is not
connected then it is disconnected.
Example 4.1
1 2 3
find:
1. the adjacency matrix A;
2. the matrix giving the number of 3 step walks.a
a
This problem is from the film Good Will Hunting where M.I.T. professor Gerald Lambeau poses this problem to
his class of graduate students claiming that it is “an advanced Fourier system” (there is no such thing as a ‘Fourier
system’) and that whomever solves it will have their name mentioned in M.I.T. Tech.
Solution:
1.
0 1 0 1
1 0 2 1
A=
0 2 0 0
1 1 0 0
4
B D
3 6
A 1 F
6
4 3
C E
5
The elements in the adjacency matrix for a weighted graph are the weights assigned to the edges, i.e.,
[A]ij = w(i, j). For example, the adjacency matrix for the weighted graph in figure 4.3 is
A B C D E F
A 0 3 4 0 0 0
B 3 0 0 4 6 0
A= C
4 0 0 0 5 0
D
0 4 0 0 1 6
E 0 6 5 1 0 3
F 0 0 0 6 5 0
1
B D
5 3
4
A 2 -1 F
-2 1
C E
5
The adjacency matrix for a directed graph may not be symmetric. For example, the edge joining nodes A
to B has weight w(A, B) = 5 but there is no edge joining B to A so w(B, A) = 0. The adjacency matrix
for the weighted graph shown in figure 4.4 is
A B C D E F
A 0 5 −2 0 0 0
B 0 0 0 1 0 0
A= C
0 2 0 4 5 0
D 0
0 0 0 −1 3
E 0 0 0 0 0 1
F 0 0 0 0 0 0
4.1.5 Trees
A tree is a connected graph, G, that satisfies the following:
• Node G contains no cycles;
• adding an edge between any two nodes in G a cycle is created;
• removing any edge in G creates a disconnected graph;
• any two nodes can be connected by a single path;
• if G has n nodes then it has n − 1 edges.
For example, the graph in figure 4.5 is a tree.
A D G
B F
C E H
A minimum spanning tree is a spanning tree for a weighted graph where the sum of the weights
of the edges are the minimum possible for the tree.
1. Remove node A from openList. Node A is not in closedList so we add it. Node B is adjacent to
node A and not in closedList so we add it to closedList and to the spanning tree.
A B C
D E F
G H I
2. Remove node B from openList. Node B is not in closedList so we add it to closedList and to the
spanning tree (node A is the parent node). Nodes A, C, D and E are adjacent to node B but node
A is in closedList so we add nodes C, D, E to openList
A B
3. Remove node E from openList. Node E is not in closedList so we add it to closedList and the
spanning tree (node B is the parent node). Nodes B, C, D, F and H are adjacent to node E but
node B is in closedList so we add nodes C, D, F, H to openList
A B E
4. Remove node H from openList. Node H is not in closedList so add it to closedList and to the
spanning tree (node E is the parent node). Nodes D, E, G and I are adjacent to node H but node
E is in closedList so add nodes D, G, I to openList
A B E H
5. Remove node I from openList. I is not in closedList so add it to closedList and to the spanning
tree (node H is the parent node). Nodes F and H are adjacent to I but H is in closedList so add
F to openList
A B E H I
6. Remove F from openList. F is not in closedList so add it to closedList and to the spanning tree
(node I is the parent node). Nodes E and I are adjacent to F but both are in closedList so we
cannot add them
A B E H I F
7. Remove G from openList. G is not in closedList so add it to closedList and to the spanning tree
(node H is the parent node). Node H is adjacent to G but is in closedList so we cannot add it to
openList
I F
A B E H
8. Remove D from openList. D is not in closedList so add it to closedList and to the spanning tree
(node H is the parent node). Nodes B, E and H are adjacent to D but are in closedList so we
cannot add them
I F
A B E H G
9. Remove F from openList. F is in closedList so we do not add it to closedList or the spanning tree.
10. Remove D from openList. D is in closedList so we do not add it to closedList or the spanning tree.
11. Remove C from openList. C is not in closedList so we add it to closedList and to the spanning
tree (node E is the parent node). All nodes are now in closedList so the algorithm terminates.
I F
H G
A B E
C D
The ordering of the nodes by the depth-first search algorithm starting at node A is
Example 4.2
Starting at node A, use depth-first search to produce a spanning tree for the graph below.
B
D
E
C
Solution:
D C
B E
# Initialise lists
closedList , openList , parent = [] , [ startnode ] , [ -1] * len ( A )
while openList :
% Initialise lists
closedList = []; openList = [ startnode ]; parent = - ones (1 , size (A , 1) ) ;
end
3. Create an n-element list parent = [∅, . . . ∅] which contains null entries. This will contain the parent
nodes for the nodes added to openList.
4. If openList 6= ∅ remove the first node u from openList and check whether it is in closedList. If u
is not in closedList add it to closedList and add u to the spanning tree.
5. Append all of the nodes v adjacent to u that are not in closedList to openList and, if this is the
first time a node has been added to openList, set their parent node to u.
6. Repeat steps 3 and 4 until the openList is empty.
Note that the breadth-first search algorithm is very similar to the depth-first search algorithm, the difference
being is that we remove the first node from the list. For example, consider applying breath-first search to
the graph in figure 4.6 with A as the start node.
1. Node A is the start node so we initialise the closedList and openList lists
2. Remove node A from openList. Node A is not in closedList so add it to closedList and to the
spanning tree. Node B is adjacent to node A and not in closedList so it is added to openList
3. Remove node B from openList. Node B is not in closedList so add it to closedList and to the
spanning tree (node A is the parent node). Nodes A, C, D and E are adjacent to node B but node
A is in closedList so add nodes C, D, E to openList
A B
4. Remove node C from openList. Node C is not in closedList so add it to closedList and to the
spanning tree (node B is the parent node). Nodes B and E are adjacent to node C but node B is
in closedList so add node E to openList
A B C
5. Remove node D from openList. Node D is not in closedList so add it to closedList and to the
spanning tree (node B is the parent node). Nodes B, E and H are adjacent to node D but node B
is in closedList so add nodes E, H to openList
A B
6. Remove E from openList. Node E is not in closedList so add it to closedList and to the spanning
tree (node B is the parent node). Nodes B, C, D, F and H are adjacent to node E but nodes B,
D and C are in closedList so add nodes F, H to openList
A B D
9. Remove node H from openList. Node H is not in closedList so add it to closedList and to the
spanning tree (node D is the parent node). Nodes D, E, G and I are adjacent to node H but nodes
D and E are in closedList so add nodes G, I to openList
A B D H
10. Remove node F from openList. Node F is not in closedList so add it to closedList and to the
spanning tree. Nodes E and I are adjacent to node F but node E is in closedList so add node I to
openList
A B D H
E F
12. Remove node G from openList. Node G is not in closedList so add it to closedList and to the
spanning tree (node H is the parent node). Node H is adjacent to node G but is already in closedList
so do not add to openList
A B D H G
E F
13. Remove Node I from openList. Node I is not in closedList so add it to closedList and to the
spanning tree (node H is the parent node). Nodes F and H are adjacent to node I but are already
in closedList so do not add to openList. All nodes are now in closedList so the algorithm terminates.
A B D H
E F
Example 4.3
Starting at node A, use breadth-first search to produce a spanning tree for the graph below.
B
D
E
C
Solution:
A B E
Here the underline (0, ∅) denotes that the node has been added to closedList.
Iteration 2 (figure 4.7(c)):
• The node in openList with the smallest distance is node C so we move this to closedList;
• Node B: d(C) + w(C, B) = 2 + 2 = 4 < 5 so we update d(B) = 4 and parent(B) = C;
• Node D: d(C) + w(C, D) = 2 + 4 = 6 < ∞ so we update d(D) = 6 and parent(D) = C;
• Node E: d(C) + w(C, E) = 2 + 5 = 7 < ∞ so we update d(E) = 7 and parent(E) = C.
iteration A B C D E F
0 (0, ∅) (∞, ∅) (∞, ∅) (∞, ∅) (∞, ∅) (∞, ∅)
1 (0, ∅) (5, A) (2, A) (∞, ∅) (∞, ∅) (∞, ∅)
2 (0, ∅) (4, C) (2, A) (6, C) (7, C) (∞, ∅)
• The node in openList with the smallest distance is node B so we move this to closedList;
• Node D: d(B) + w(B, C) = 4 + 1 = 5 < 6 so we update d(D) = 5 and parent(D) = B .
iteration A B C D E F
0 (0, ∅) (∞, ∅) (∞, ∅) (∞, ∅) (∞, ∅) (∞, ∅)
1 (0, ∅) (5, A) (2, A) (∞, ∅) (∞, ∅) (∞, ∅)
2 (0, ∅) (4, C) (2, A) (6, C) (7, C) (∞, ∅)
3 (0, ∅) (4, C) (2, A) (5, B) (7, C) (∞, ∅)
4 1 5
B D
5 3
4
0 A 2 1 F 7
2 1
C E
2 5 6
∞ 1 ∞ 5 1 ∞
B D B D
5 3 5 3
4 4
0 A 2 1 F ∞ 0 A 2 1 F ∞
2 1 2 1
C E C E
∞ 5 ∞ 2 5 ∞
(a) Initial graph (b) iteration 1
4 1 6 4 1 5
B D B D
5 3 5 3
4 4
0 A 2 1 F ∞ 0 A 2 1 F ∞
2 1 2 1
C E C E
2 5 7 2 5 7
(c) iteration 2 (d) iteration 3
4 1 5 4 1 5
B D B D
5 3 5 3
4 4
0 A 2 1 F 8 0 A 2 1 F 7
2 1 2 1
C E C E
2 5 6 2 5 6
(e) iteration 4 (f) iteration 5
Figure 4.7: Implementation of Dijkstra’s algorithm to find the shortest path from A to F .
Example 4.4
Use Dijkstra’s algorithm to find the shortest path between nodes A and F in the graph below.
5 2
A C E
1
3 3 4
B D F
2 3
Solution:
iteration A B C D E F
0 (0, ∅) (∞, ∅) (∞, ∅) (∞, ∅) (∞, ∅) (∞, ∅)
1 (0, ∅) (3, A), (5, A) (∞, ∅) (∞, ∅) (∞, ∅)
2 (0, ∅) (3, A) (4, B) (5, B) (∞, ∅) (∞, ∅)
3 (0, ∅) (3, A) (4, B) (5, B) (6, C) (∞, ∅)
4 (0, ∅) (3, A) (4, B) (5, B) (6, C) (3, D)
5 (0, ∅) (3, A) (4, B) (5, B) (6, C) (3, D)
6 (0, ∅) (3, A) (4, B) (5, B) (6, C) (3, D)
4
5 2
0 A C E 6
1
3 3 4
3 B D F 8
2 3
5
1
B D
5 3
4
A 2 -1 F
-2 1
C E
5
between nodes A and F in figure 4.9. We initialise the distances to ∞ except for the start node A
(figure 4.10(a))
step A B C D E F
0 (0,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅)
Iteration 1:
• Node A is the start node so we don’t update d(A);
• Node B: d(A) + w(A, B) = 0 + 5 = 5 < ∞ and d(C) + w(C, B) = ∞ + 2 = ∞ so we update
d(B) = 5 and parent(B) = A;
• Node C: d(A) + w(A, C) = 0 − 2 = −2 < ∞ so we update d(C) = −2 and parent(C) = A;
• Node D: d(B) + w(B, D) = ∞ + 1 = ∞ and d(C) + w(C, D) = ∞ + 4 = ∞ so we do not update
d(D) or parent(E);
• Node E: d(C) + w(C, E) = ∞ + 5 = ∞ and d(D) + w(D, E) = ∞ − 1 = ∞ so we do not update
d(E) or parent(E);
• Node F : d(D) + w(D, F ) = ∞ + 3 = ∞ and d(E) + w(E, F ) = ∞ + 1 = ∞ so we do not update
d(F ) or parent(F ).
iteration A B C D E F
0 (0,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅)
1 (0,∅) (5,A) (-2,A) (∞,∅) (∞,∅) (∞,∅)
After the first step the distances for all paths at most 1 edge from the start node are found. This means
we don’t need to check nodes A or C again.
Iteration 2:
• Node B: d(A) + w(A, B) = 0 + 5 = 5 6< 5 and d(C) + w(C, B) = −2 + 2 = 0 < 5 so we update
d(B) = 0 and parent(B) = C;
• Node D: d(B) + w(B, D) = 5 + 1 = 6 < ∞ and d(C) + w(C, D) = −2 + 4 = 2 < ∞ so we update
d(D) = 2 and parent(D) = C;
• Node E: d(C) + w(C, E) = −2 + 5 = 3 < ∞ and d(D) + w(D, E) = ∞ − 1 = ∞ so we update
d(E) = 3 and parent(E) = C;
• Node F : d(D) + w(D, F ) = ∞ + 3 = ∞ and d(E) + w(E, F ) = ∞ + 1 = ∞ so we do not update
d(F ) or parent(F ).
iteration A B C D E F
0 (0,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅)
1 (0,∅) (5,A) (-2,A) (∞,∅) (∞,∅) (∞,∅)
2 (0,∅) (0,C) (-2,A) (2,C) (3,C) (∞,∅)
After the second iteration the distances for all paths at most 2 edges from the start node are found so we
no longer need to check node B again.
Iteration 3:
• Node D: d(B) + w(B, D) = 0 + 1 = 1 < 2 and d(C) + w(C, D) = −2 + 4 = 2 6< 2 therefore we
update d(D) = 1 and parent(D) = B;
• Node E: d(C) + w(C, E) = −2 + 5 = 3 6< 3 and d(D) = 2 − 1 = 1 < 3 therefore we update
d(E) = 1 and parent(E) = C;
• Node F : d(D) + w(D, F ) = 2 + 3 = 5 < ∞ and d(E) + w(E, F ) = 3 + 1 = 4 < ∞ therefore we
update d(F ) = 4 and parent(F ) = E.
iteration A B C D E F
0 (0,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅)
1 (0,∅) (5,A) (-2,A) (∞,∅) (∞,∅) (∞,∅)
2 (0,∅) (0,C) (-2,A) (2,C) (3,C) (∞,∅)
3 (0,∅) (0,C) (-2,A) (1,B) (1,C) (4, E)
Now we know the distances to all paths at most 3 edges from node A so we no longer need to check node
D again.
Iteration 4:
• Node E: d(C) + w(C, E) = −2 + 5 = 3 > 1 and d(D) = 1 − 1 = 0 < 1 therefore update d(E) = 0
and parent(E) = D;
• Node F : d(D) + w(D, F ) = 1 + 3 = 4 6< 4 and d(E) + w(E, F ) = 1 + 1 = 2 < 4 therefore we
update d(F ) = 2 and the parent node is unchanged.
iteration A B C D E F
0 (0,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅)
1 (0,∅) (5,A) (-2,A) (∞,∅) (∞,∅) (∞,∅)
2 (0,∅) (0,C) (-2,A) (2,C) (3,C) (∞,∅)
3 (0,∅) (0,C) (-2,A) (1,B) (1,C) (4, E)
4 (0,∅) (0,C) (-2,A) (1,B) (0,D) (2, E)
Now we know the distances to all paths at most 4 edges from node A so we no longer need to check node
E.
Iteration 5:
• Node F : d(D) + w(DF ) = 1 + 3 = 4 and d(E) + w(EF ) = 0 + 1 = 1 therefore we update d(F ) = 1
and the parent node is unchanged.
step A B C D E F
0 (0,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅) (∞,∅)
1 (0,∅) (5,A) (-2,A) (∞,∅) (∞,∅) (∞,∅)
2 (0,∅) (0,C) (-2,A) (2,C) (3,C) (∞,∅)
3 (0,∅) (0,C) (-2,A) (1,B) (1,C) (4, E)
4 (0,∅) (0,C) (-2,A) (1,B) (0,D) (2, E)
5 (0,∅) (0,C) (-2,A) (1,B) (0,D) (1, E)
Since we have done n − 1 steps of the algorithm none of the distances can be updated so we stop here.
The shortest path is found by back tracking from the end node and prepending the parent nodes in the
same way as used in Dijkstra’s algorithm which gives P = (A, C, B, D, E, F ) (figure 4.11).
∞ 1 ∞ ∞ ∞
B D 1
B D
5 3 5 3
4 4
0 A 2 -1 F ∞ 0 A 2 -1 F ∞
-2 1 -2 1
C E C E
∞ 5 ∞ ∞ 5 ∞
(a) initial graph (b) iteration 1
5 1 ∞ 0 1 1
B D B D
5 3 5 3
4 4
0 A 2 -1 F ∞ 0 A 2 -1 F ∞
-2 1 -2 1
C E C E
−2 5 ∞ −2 5 3
-2 1 -2 1
C E C E
−2 5 0 −2 5 0
(e) iteration 4 (f) iteration 5
Figure 4.10: Implementation of the Bellman-Ford algorithm to find the shortest path from A to
F on a directed graph with negative weights.
0 1 1
B D
5 3
4
0 A 2 -1 F 1
-2 1
C E
−2 5 0
Figure 4.11: The shortest path from node A to node F found using the Bellman-Ford algorithm.
2
A B
−4
2
C D
1
To find out whether a directed graph has a negative cycle we simply attempt to relax the distance values
for the node once again. If one of the distances can be reduced further then we have a negative cycle.
This is because after n − 1 iterations all distances should be minimised, so if we can relax a distance value
then we must have a negative cycle.
where g(n) is the distance from the start node1 and h(n) is a heuristic2 that estimates the cost of the
cheapest path from node n to the end node.
Given a weighted graph G and the start and end nodes, startnode and endnode, the steps of the A*
algorithm are:
1. Create two lists, openList and closedList, where openList will contain the vertices that have been
evaluated by the heuristic function and closedList contains those nodes that have been visited. Put
the start node startnode into openList.
2. Create n-element lists parent = [∅, . . . ∅], g = [∞, . . . , ∞] and f = [∞, . . . , ∞]. parent contains
the parent nodes, g contains the shortest distance from the start node and f contains the estimated
cost.
3. Pick node u from the openList list with the smallest value of f (u) and move this to the closedList
list.
4. If u = endnode exit the algorithm.
5. For each node v that is adjacent to u and not in closedList. If the distance from the start node through
u is less than g(v) then update parent(v) = u, g(v) ← g(u) + w(u, v) and f (v) ← g(v) + h(v).
If v is not in openList then add it.
6. Repeat steps 3 and 4 until the end node has been moved to closedList.
The shortest path is found using back tracking through the parent list in the same way as in Dijkstra’s and
the Bellman-Ford algorithm.
For example, consider the implementation of the A* algorithm to the shortest between nodes A and G in
the graph shown in figure 4.13. Here the heuristic values have been given for each node.
We start by putting the start node A into openList and initialising g(A) = 0 and f (A) = h(A) = 7.
Iteration 1 (figure 4.14(a)):
• The node in openList with the minimum f (n) value is node A so we move this to closedList;
• Node B is not in openList so we add it and set parent(B) = A, g(B) = g(A)+w(A, B) = 0+4 = 4
and f (B) = g(B) + h(B) = 4 + 5 = 9;
• Node C is not in openList so we add it and set parent(C) = A, g(C) = g(B)+w(A, C) = 0+6 = 6
and f (C) = g(C) + h(C) = 6 + 6 = 12;
• Node D is not in openList so we add it and set parent(D) = A, g(A) = g(A)+w(A, D) = 0+4 = 4
and f (D) = g(D) + h(D) = 4 + 4 = 8:
1
So far in this chapter I have used d(n) to denote the distance from the start node which is the game as g(n). Here I have
chosen to use g(n) as this is what is commonly used when presenting the A* algorithm.
2
A heuristic is a method for giving a solution to a problem that does not necessarily give the optimum solution but one
that is good enough for our needs.
h=5
B
4 5
h=3
3
h=7 A E
4 4 4
D G h=0
6
h=4 5
6 6 7
8
C F
h=6 h=4
Figure 4.13: A weighted graph with heuristic values for each node.
g = 4 g = 4
h = 5 h = 5
f = 9 f = 9
B B g = 8
4 5 4 5 h = 3
h = 3
3 f = 11
h = 7 A E h = 7 A E
4 4 4 4 3 4 4
D G h = 0 D G h = 0
6 6
g = 4 5 g = 4 5
6 h = 4 6 7 h = 4 6 7
f = 8
f = 8 6
8 8
C F C F
g = 6 g = 6 g = 10
h = 4 h = 6 h = 4
h = 6
f = 12 f = 14
f = 12
Figure 4.14: Implementation of the A* algorithm to find the shortest path between nodes A and
G.
4.6.1 Heuristics
One of the problems with implementing the A* algorithm is how do we calculate the value of the heuristic
h(n)? The heuristic is the cost of extending the path to the end node but since the algorithm adds one
node at a time we do not know what nodes will be selecting in future steps so we do not know what this
cost will be.
Some of the possible solutions for calculating the heuristic are:
• Exact heuristic – h(n) is calculated for all possible paths from each node to the end node is
calculated. Whilst this is possible for graphs will a small number of nodes the computational cost
soon becomes prohibitive for graphs with relatively modest number of nodes.
• Manhattan distance – where nodes are positioned due to some location metric, e.g., the location
of a road junction on a map or a position in a virtual world, we calculates h(n) are the sum of the
distance in the x and y directions. If the start and end nodes have co-ordinates (xs , ys ) and (xe , ye )
respectively then the heuristic is calculated using
This method is named after the block like structure of roads in Manhattan in New York.
• Euclidean distance – the straight line distance between the location of two nodes. If the start and
end nodes have co-ordinates (xs , ys ) and (xe , ye ) respectively then the heuristic is calculated using
q
h(n) = (xe − xs )2 + (ye − ys )2 .
(400, 590) L
P (460, 400)
(220, 510) K O
(460, 480) M (550, 420)
(80, 440) G N (590, 420)
(300, 400) J
(100, 340) F
(−180, 320) C I (320, 320)
(120, 240) E
H (380, 180)
(−80, 120) B
D (200, 100)
(0, 0) A
Applying the A* algorithm to this graph using the Euclidean distance to node P as the heuristic results in
the graphs shown in figure 4.16.
Figure 4.16: Implementing the A* algorithm to find the shortest path between nodes A to P .
Target
NPC
Figure 4.17: The path a non-play character will take to reach a target.
Consider the implementation of the A* algorithm for the path finding problem shown in figure 4.17 with
h(n) calculated using the Manhattan distance. The start node has co-ordinates (1, 3) and is moved to
closedList = {(1, 3)}. The start node has 5 adjacent nodes with co-ordinates (1, 4), (2, 4), (2, 3), (2, 2)
and (1, 2).
• (1, 4): g(1, 4) = 0 + 1 = 1, h(1, 4) = |8 − 1| + |4 − 4| = 7, f (1, 4) = 1 + 7 = 8;
• (2, 4): g(2, 4) = 0 + 1.4 = 1.4, h(2, 4) = |8 − 2| + |4 − 4| = 6, f (2, 4) = 1.4 + 6 = 7.4;
• (2, 3) : g(2, 3) = 0 + 1 = 1, h(2, 3) = |8 − 2| + |4 − 3| = 7, f (2, 3) = 1 + 7 = 8;
• (2, 2): g(2, 2) = 0 + 1.4 = 1.4, h(2, 2) = |8 − 2| + |4 − 2| = 8, f (2, 2) = 1.4 + 8 = 9.4;
• (1, 2): g(1, 2) = 0 + 1 = 1, h(1, 2) = |8 − 1| + |4 − 2| = 9, f (1, 2) = 1 + 9 = 10.
All of the g(n) values were updated for these nodes so we record their parent node as (1, 3) and add them
to openList. The node in openList with the smallest f (n) value is (2, 4) so we move this to closedList
(figure 4.18(a))
closedList = {(1, 3), (2, 4)}, openList = {(1, 4), (2, 3), (2, 2), (1, 2)}.
The node at (2, 4) has 4 adjacent nodes not in closedList with co-ordinates (1, 5), (2, 5), (2, 3) and (1, 4).
• (1, 5): g(1, 5) = 1.4 + 1.4 = 2.8, h(1, 5) = |8 − 1| + |4 − 5| = 8, f (1, 5) = 2.8 + 8 = 10.8;
• (2, 5): g(2, 5) = 1.4 + 1 = 2.4, h(2, 5) = |8 − 2| + |4 − 5| = 7, f (2, 5) = 2.4 + 7 = 9.4;
• (2, 3): g(2, 3) = 1.4 + 1 = 2.4 which is not less than the current value of g(2, 3) = 1 so f (2, 3) = 8
is unchanged;
• (1, 4): g(1, 4) = 1.4 + 1 = 2.4 which is not less than the current value of g(1, 4) = 1 so f (1, 4) = 8
is unchanged.
The nodes at (1, 5) and (2, 5) had their g(n) values updated so we update their parent node to the node
at (2, 4) and add them to openList. The node in openList with the smallest f (n) value are nodes (2, 3)
and (1, 4). We could select any of these to going into closedList, we will choose (1, 4) (choosing (2, 3)
will not affect the final path) so we move this to closedList (figure 4.18(b))
closedList = {(1, 3), (2, 4), (1, 4)}, openList = {(2, 3), (2, 2), (1, 2)}.
The node at (1, 4) has 3 adjacent nodes not in closedList with co-ordinates (1, 5), (2, 5), (2, 3).
• (1, 5): g(1, 5) = 1 + 1 = 2 which is less than the current value g(1, 5) = 7 so we update f (1, 5) =
2 + 7 = 9;
• (2, 5): g(2, 5) = 1 + 1.4 = 2.4, h(2, 5) = |8 − 2| + |4 − 5| = 7, f (2, 5) = 2.4 + 7 = 9.4;
• (2, 3): g(2, 3) = 1 + 1.4 = 2.4 which is not less than the current value of g(2, 3) = 1 so f (2, 3) = 8
is unchanged.
The nodes at (1, 5) and (2, 5) had their g(n) values updated so we update their parent node to the node
at (1, 4) and add them to openList. The node in openList with the smallest f (n) value is the node at
(2, 5) so we move this to closedList (figure 4.18(c))
closedList = {(1, 3), (2, 4), (1, 4), (2, 5)}, openList = {(2, 3), (2, 2), (1, 2), (1, 5), (2, 5)}.
g = 2.4 g = 2.4
5 5 h = 8 h = 7
f = 10.4 f = 9.4
g = 1 g = 1.4 g = 1 g = 1.4
4 h = 7
f = 8
h = 6
f = 7.4
4 h = 7
f = 8
h = 6
f = 7.4
g = 0 g = 1 g = 0 g = 1
3 h = 8
f = 8
h = 7
f = 8
3 h = 8
f = 8
h = 7
f = 8
g = 1 g = 1.4 g = 1 g = 1.4
2 h = 9 h = 8
f = 10 f = 9.4
2 h = 9 h = 8
f = 10 f = 9.4
1 1
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
(a) iteration 0 (b) iteration 1
g = 2 g = 2.4 g = 3.4 g = 2 g = 2.4 g = 3.4 g = 4.4 g = 5.4
5 h = 8 h = 7 h = 6
f = 10 f = 9.4 f = 9.4 5 h = 8 h = 7 h = 6 h = 5 h = 4
f = 10.8 f = 9.4 f = 9.4 f = 9.4 f = 9.4
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
(c) iteration 2 (d) iteration 10
Continuing to apply the algorithm until the end node is reached gives
closedList = {(1, 3), (2, 4), (1, 4), (2, 5), (3, 5), (4, 5), (5, 4), (5, 3), (5, 2), (6, 2), (7, 2), (8, 3), (8, 4)}
The path between the NPC and the target is found by back tracking through the parent nodes to give the
path shown in figure 4.19. Note that the node at (1, 4) was in closedList but not in the path.
1
1 2 3 4 5 6 7 8 9
Figure 4.19: The path between the NPC and the target is found by backtracking through the
parent nodes.
V = {A, B, C, D, E, F },
E = {(A, C), (A, D), (A, F ), (B, C), (B, D), (C, E), (D, E), (E, F )}.
A B C D E F G H I
A 0 1 0 1 1 0 0 0 0
B 1
0 1 0 1 0 0 0 0
C 0
1 0 0 1 0 0 0 0
D 1
0 0 0 1 0 1 0 0
E 1 1 1 1 0 1 0 1 1.
F 0
0 0 0 1 0 0 0 1
G 0
0 0 1 0 0 0 0 0
H 0 0 0 0 1 0 0 0 0
I 0 0 0 0 1 1 0 0 0
A B C D E F G
A 0 4 3 7 4 0 0
B 4
0 0 2 5 0 0
C 3
0 0 2 0 0 0
D 7 2 2 0 5 3 0 .
E 4
5 0 5 0 0 6
F 0 0 0 3 0 0 5
G 0 0 0 0 6 5 0
A B C D E
A
0 2 1 0 0
B 0
0 −2 0 4
C 0
2 0 3 0
D 0 −1 0 0 2
E 0 0 0 0 0
Use the Bellman-Ford algorithm to determine the shortest path between nodes A and E.
Exercise 4.10. Use the A* algorithm to determine the shortest path between the start cell and the target
cell.
4
target
2
start
1 2 3 4 5
Exercise 4.11. Below is a map of a part of England with the towns and cities on the main road network
marked using nodes of a graph and the distances, in kilometres, are given as weights on the graph. Using
the Manhattan distance to calculate the heuristic, use the A* algorithm to find the shortest path between:
(a) London to Manchester;
(b) Exeter to Rugby.
Manchester Sheffield
61
111
143
125
Birmingham 60 Rugby Huntingdon
103
116 87 80
81
Bicester
Bristol 70 30
Hermitage 80 London
114 95
110
Southampton
Exeter
Bellman, R. (1958). “On a routing problem”. In: Quarterly of Applied Mathematics 16.
Dijkstra, E.W. (1959). “A note on two problems in connexion with graphs”. In: Numerische Mathematik
1, pp. 269–271.
Ford, L.R. (1956). Network Flow Theory. Tech. rep. RAND Corporation.
Hart, P.E., Nilsson, N.J., and Raphael, B. (1968). “A Formal Basis for the Heuristic Determination of
Minimum Cost Paths”. In: IEEE Transactions on Systems Science and Cybernetics 2.4, pp. 100–107.
Hoare, C.A.R. (1961). “Algorithm 64: Quicksort”. In: Comm. ACM 7.4, p. 321.
Karnaugh, M. (1953). “The map method for synthsis of combinational logic circuits”. In: Transactions of
the American Institute of Electrical Engineers, Part I: Communication and Electronics 5.72, pp. 593–599.
Tocci, R.J., Widmer, N.S., and Moss, G.L. (2007). Digital systems.
Wikipedia contributors (2001). Algorithm — Wikipedia, The Free Encyclopedia. url: https : / / en .
wikipedia.org/wiki/Algorithm (visited on 02/07/2022).
87
Appendix A
Exercise solutions
Solution 1.2.
Solution 1.3.
Solution 1.4.
Solution 1.7.
1 C E 4 F
1 0 A 5 4
carry 1 1
sum 2 D 8 A 3
Solution 1.8.
(a) (b)
p q ¬p ∨ (p ∧ q) p q ¬(p ∨ q) ∧ (p ∨ ¬q)
0 0 1 0 0 0
0 1 0 0 1 0
1 0 1 1 0 0
1 1 1 1 1 1
89
Appendix A. Exercise solutions Back to Table of Contents
(c)
Solution 1.9.
(a) (b)
¬p ∨ (p ∧ q) ¬(p ∨ q) ∧ (p ∨ ¬q)
≡ (¬p ∨ p) ∧ (¬p ∨ q) (distributive law) ≡ ¬p ∧ ¬q ∧ (p ∨ ¬q) (De Morgan’s law)
≡ 1 ∧ (¬p ∨ q) (complement law) ≡ ¬p ∧ ¬q (absorption law)
≡ ¬p ∨ q (identity law)
(c)
Solution 2.1.
A B C Q A B C Q A B C D Q
0 0 0 1 0 0 0 0 0 0 1 1 0
0 1 1 0 0 1 0 1 0 1 1 0 1
1 0 1 0 1 0 0 0 1 0 1 1 0
1 1 1 0 1 1 1 1 1 1 0 0 1
Solution 2.2.
(a) (b)
A A
Q B Q
B
C
(c)
A
Q
Solution 2.3.
(a)
A · (A + B) ≡ A · A + A · B (distributivity law)
≡0+A·B (complement law)
≡A·B (definition of AND)
(b)
(A + A · B) · (C + B · C) ≡ A · C + A · B · C + A · B · C + A · B · C (distributivity law)
=A·C +A·B·C +A·B·C (idempotence law).
(c)
(d)
(A + B · C) · (A + B) ≡ A · A + A · B + A · B · C + B · B · C (distributivity law)
≡A+A·B+A·B·C +B·B·C (idempotence law)
≡A+A·B+A·B·C +0·C (complement law)
≡A+A·B+A·B·C (definition of AND).
Solution 2.4. Q ≡ B · C
B
Q
Solution 2.5. We need to show that the other logic gates can be constructed from NOR gates.
• NOT gate: A + A ≡ A;
• AND gate: A + A + B + B ≡ A + B ≡ A · B ≡ A · B;
• OR gate: A + B + A + B ≡ A + B = A + B;
• XOR gate:
A+A+B+A+B+B+A+A+B+A+B+B
≡A+A+B+A+B+B
≡A+A+B+A+B+B
≡A·A+B+A+B·B
≡ A · (A + B) + B · (A + B)
≡A·A+A·B+A·B+B·B
≡A·B+A·B
≡ A ⊕ B.
• NAND gate: A + A + B + B + A + A + B + B ≡ A + B + A + B ≡ A + B ≡ A + B ≡ A · B
Solution 2.6.
Solution 2.7.
A A
B 0 1 B 0 1
0 1 0 0 1 0
1 1 1 1 1 1
AB AB
Cin 00 01 11 10 Cin 00 01 11 10
0 1 0 1 1 0 1 0 1 1
1 0 1 0 0 1 0 1 0 0
AB AB
Cin 00 01 11 10 Cin 00 01 11 10
0 1 0 0 1 0 1 0 0 1
1 0 1 1 0 1 0 1 1 0
Solution 2.8.
(a)
A
C f (A, B, C)
(b)
C f (A, B, C)
A.3 Algorithms
These are the solutions to the exercises on graph theory on page 51.
Solution 3.1.
1.
Solution 3.2.
Pass 1: Pass 2:
k , swap = 0 , True
while swap :
swap = False
for i in range ( len ( X ) - k - 1) :
if X [ i ] > X [ i +1]:
X [ i ] , X [ i +1] = X [ i +1] , X [ i ]
swap = True
k += 1
return X
# Define list
X = [5 , 1 , 3 , 6 , 2 , 4]
# Sort list
print ( f ’X = { X }\ nsorted X = { bubblesort ( X ) } ’)
MATLAB:
% Define list
X = [5 , 1 , 3 , 6 , 2 , 4]
% Sort list
bubblesort ( X )
function X = bubblesort ( X )
k = 0;
swap = true ;
while swap
swap = false ;
for i = 1 : length ( X ) - k - 1
if X ( i ) > X ( i + 1)
t = X(i);
X ( i ) = X ( i + 1) ;
X ( i + 1) = t ;
swap = true ;
end
end
k = k + 1;
end
end
Solution 3.6.
(a)
function minrecursion(X, n)
if n = 1 then
return X(0) . assuming zero indexing (X(0) is the first element)
else
Xmin ← minrecursion(X, n − 1)
if Xmin < X(n − 1) then
return Xmin
else
return X(n − 1)
end if
end if
end function
(b) Call the function minrecursion([5, 6, 4, 7, 3], 5)
• minrecursion([5, 6, 3, 7, 4], 5): n > 1 so call minrecursion([5, 6, 3, 7, 4], 4)
• minrecursion([5, 6, 3, 7, 4], 4): n > 1 so call minrecursion([5, 6, 3, 7, 4], 3)
• etc.
• minrecursion([5, 6, 3, 7, 4], 1): n = 1 so return Xmin = 5
• minrecursion([5, 6, 3, 7, 4], 2): Xmin = 5 < X(1) = 6 so return Xmin = 5
• minrecursion([5, 6, 3, 7, 4], 3): Xmin = 5 > X(2) = 4 so return Xmin = 4
• minrecursion([5, 6, 3, 7, 4], 4): Xmin = 4 < X(3) = 7 so return Xmin = 4
• minrecursion([5, 6, 3, 7, 4], 5): Xmin = 4 > X(4) = 3 so return Xmin = 3
(c) Python:
def minrecursion (X , n ) :
if n == 0:
return X [0]
else :
Xmin = minrecursion (X , n - 1)
if Xmin < X [n -1]:
return Xmin
else :
return X [n -1]
MATLAB:
fprintf ( ’ Xmin = % i ’ , minrecursion ([5 , 6 , 4 , 7 , 3] , 5) )
if n == 1
Xmin = X (1) ;
else
Xmin = minrecursion (X , n - 1) ;
if Xmin > X ( n )
Xmin = X ( n ) ;
end
end
end
Solution 3.7.
(a) 1 2
2n + 21 n;
(b) 1 3
3n + 31 n2 − 56 n;
(c) 1 3
3n + 21 n2 − 56 n;
(d) O(n3 ).
Solution 4.1.
B
A C
F D
(b) P = (E, C, B, D, A, F );
This graph does not have an Eulerian cycle since the number of nodes with an odd degree is not zero or two.
Solution 4.4.
0 0 1 1 0 1 22 16 0 0 22 0
0 0 1 1 0 0 16 12 0 0 16 0
1 1 0 0 1 0 0 0 22 22 0 16
A=
1
, A4 = .
1 0 0 1 0
0
0 22 22 0 16
0 0 1 1 0 1 22 16 0 0 22 0
1 0 0 0 1 0 0 0 16 16 0 12
Solution 4.5.
Solution 4.6.
(a)
A B C
D E F
G H I
(c)
A B C
D E F
G H I
Solution 4.7.
(c)
A B C
D E F
G H I
Solution 4.8.
4 9
5
B E
4 5 6
2
7
0 A D G 13
5
3 2 3 5
C F
3 8
Solution 4.9.
2 4
B E 5
2 -1
2
0 A 2 -2 D
3
1 3
C
0
Solution 4.10. Shortest path: P = ((1, 2), (2, 3), (2, 4), (2, 5), (3, 5), (4, 5), (5, 4))
g = 1 g = 1.41
3 h = 5 h = 4
f = 6 f = 5.41
g = 0 g = 1
2 h = 6 h = 5
f = 6 f = 6
g = 1 g = 1.41
1 h = 7 h = 6
f = 8 f = 7.41
1 2 3 4 5
Solution 4.11.
(a) Shortest path: P = (E, H, G, J); (b) Shortest path: P = (A, C, D, E, I).
g = 274 61 g = 228 61
h = 0 J K h = 70 J K
f = 274 f = 298
143 143
111 125 111 125
g = 103
g = 163 h = 205 g = 80 g = 230 g = 367 g = 344
h = 125 G H f = 308 I h = 265 h = 140 G H h = 60 I h = 0
f = 288 60 f = 345 f = 370 60 f = 427 f = 344
87 103 87 103
g = 81 g = 214 81 80
116 F 81 80 116
h = 120
F
h = 245
f = 326 f = 334
30 g = 114
30 g = 264
C D E h = 245 C D E h = 85
70 g = 80 80 f = 359 70 g = 184 80 f = 349
h = 270 h = 155
114 f = 350 114 f = 339
95 95
110 110
g = 110 g = 279
A h = 335 B A h = 230 B
f = 445 f = 509
101
Index Back to Table of Contents