CS1113: Foundations of Computer Science II: Course Notes
CS1113: Foundations of Computer Science II: Course Notes
Course Notes
Written by Professor Ken Brown
Academic Year 2015-16: Lecturer – Professory Barry O’Sullivan
Department of Computer Science, University College Cork
Office: 2-65, Western Gateway Building
Email: [email protected]
Course Web Page: http://osullivan.ucc.ie/teaching/cs1113
CS1113 is a continuation from CS1112, intended to provide you with the skills you need to complete
the rest of the CK401 degree program. It focuses on extended methods for logical specification
(which use sets and relations, in addition to the truth functions of propositional logic), on describing
structured collections of objects and their use in practical applications, on algorithms and on
techniques for analysing algorithms and structures.
As with CS1112, the most important thing in the module is the need to express yourself clearly and
precisely. The techniques we use are more complex than those of CS1112. We assume you are
familiar with the set notation, functions, relations and propositional logic from CS1112, that you are
able to use them to write down clear statements that say what you want, and that you are able to
understand formal statements written using that notation.
The rest of this document contains the notation and definitions that we use through the module, and
includes important results that you should know. We also give proofs that some of these results are
correct. You will need to learn how to write similar proofs. We start the section number at 5, to
emphasise that this material continues on from where CS1112 ended.
As before, you will not be able to survive using only this document, and it is not an easy document to
read. We give it to you so that you have a clean copy of all the facts and definitions stated clearly, and
so that you do not have to spend your time in lectures writing down these definitions. You must attend
the lectures to see why we need these definitions for Computer Science, to explain what the
definitions mean, and to see how these definitions are applied. The concepts are more complex than in
CS1113, and you will need to build up your skills as you go along.
1
5. Quantified Logic
(Precise communication, specification and argument)
Definition 5.1: A predicate is a relation used in logical statements. If the predicate is an n-ary
relation, then n is the arity of the predicate.
Definition 5.2: Let P be a predicate P ⊆ S1 × S2 × ... × Sn. If (a1, a2, ..., an) ∈ P, then we say
P(a1, a2, ..., an) is true; if (a1, a2, ..., an) ∉ P, then we say P(a1, a2, ..., an) is false.
Notation: if we write P(a1, a2, ..., an) on its own, then we are claiming that P(a1, a2, ..., an) is true.
Notation: we often write predicates without specifying which elements are being evaluated, using
logical variables. So if P ⊆ {a,b,c} × {a,b,c}, then we could write P(a,b), P(x,c), P(b,y), P(x,y), and
so on, where x and y are logical variables. P(a,b) has a known truth value – it is either true or false,
depending on the definition of the predicate – but P(x,c), P(b,y) and P(x,y) do not have definite truth
values – they could be either true or false, depending on which values are assigned to x and y.
First, we will consider relations over a single set. That is, P ⊆ U, for some set U, and the statement
P(a) where a ∈ U is a claim that P(a) is true, or that a ∈ P.
Definition 5.3: The symbol ∀ is called the universal quantifier. The statement ∀x P(x) where x is a
logical variable (and where the symbol x is not used in U) is a claim that P(x) is true no matter which
element of U we assign to x. If there are one or more elements a ∈ U such that P(a) is false, then
∀x P(x) is false; otherwise it is true.
Definition 5.4: The symbol ∃ is called the existential quantifier. The statement ∃x P(x) where x is a
logical variable (and where the symbol x is not used in U) is a claim that there is at least one element
of U that we could assign to x which would make P(x) true. If there are no elements a ∈ U such that
P(a) is true, then ∃x P(x) is false; otherwise it is true.
Now consider arbitrary relations. P ⊆ Un = U×U× ... ×U, for some number n, and the statement
P(a1,a2,...,an) where ai ∈ U for each i is a claim that P(a1,a2,...,an) is true, or that (a1,a2,...,an) ∈ P.
2
Definition 5.6: The scope of a quantifier in a wff is the sub-part of the wff to which the quantifier
applies. That is, in Step 2 in Defn 5.5, the scope of ∀x and ∃x is W.
Definition 5.7:
(i) In a wff, an occurrence of variable x is bound if it is in the scope of ∀x or ∃x
(ii) In a wff, an occurrence of a variable x is free if it is not bound
Definition 5.8: If W is a wff then ∀x W is true if and only if for every constant a ∈ U when we
replace all free occurrences of x in W with a, we get a true statement. If there are one or more
elements a ∈ U such that when we replace all free occurrences of x in W with a, we get a false
statement, then ∀x W is false.
Definition 5.9: If W is a wff then ∃x W is true if and only if there is at least one constant a ∈ U such
that when we replace all free occurrences of x in W with a, we get a true statement. If there are no
elements a ∈ U such that when we replace all free occurrences of x in W with a we get a true
statement, then ∃x W is false.
Definition 5.10: Two wffs are logically equivalent if and only if no matter what relations we choose
for the predicates and no matter what set U we choose, the two wffs have the same truth value.
Theorem 5.11:
(i) ∃x (P(x) ∨ Q(x)) is logically equivalent to ∃x (P(x)) ∨ ∃x (Q(x))
(ii) ∀x (P(x) ∧ Q(x)) is logically equivalent to ∀x (P(x)) ∧ ∀x (Q(x))
Theorem 5.12:
(i) ¬∃x (P(x)) is logically equivalent to ∀x (¬P(x))
(ii) ¬∀x (P(x)) is logically equivalent to ∃x (¬P(x))
Note 5.13: from the definition of the language in 5.5, we can quantify a sequence of variables, and we
interpret the results according to Definitions 5.8 and 5.9. So if P ⊆ U×U, and we write
(i) ∀x ∀y P(x,y), we are saying that no matter which constant a we substitute for x, the sub-
statement ∀y P(a,y) is true. In turn, that means no matter what constant b we choose (which
could be the same as a or different), P(a,b) is true. So, no matter what constants a and b we
choose, P(a,b) is true.
(ii) ∃x ∃y P(x,y) says there is at least one constant, say a, such that ∃ y P(a,y) is true. That means
there is at least one constant b (which may the same as or different to a) such that P(a,b) is true.
(iii) ∀x ∃y P(x,y) says that no matter which constant a we substitute for x, the sub-statement ∃y
P(a,y) is true, which in turn means that there is at least one b such that P(a,b) is true. Therefore,
for each constant a, there is at least one constant b (which may be different for each different a)
such that P(a,b) is true.
(iv) ∃x ∀y P(x,y) says that there is at least one constant a such that the sub-statement ∀y P(a,y) is
true, which in turn means no matter which constant b we choose, P(a,b) is true.
Note 5.14: the order of the quantified variables does not have to match the order in which the
variables appear in the wff that is being quantified. E.g. we can write ∃y ∀x P(x,y), which says there
is at least one constant b such that ∀x P(x,b) is true.
3
Note 5.15: the order of the quantifiers is important. ∀x ∃y P(x,y) and ∃y ∀x P(x,y) are saying
different things.
Finally, instead of just constants or variables, we can allow functions applied to constants and
variables as the elements on which the predicates act.
∀x (P(x)→Q(x)) ∀x (P(x)→Q(x))
P(a) for some value a ¬Q(a) for some value a
in the domain in the domain
Q(a) ¬P(a)
and these rules can be extended to apply to any well-formed formula rather than just unary
predicates.
4
6. Simple Algorithms
(Algorithms and structured collections of objects)
Note: Sometimes we will allow algorithms that do not terminate, and sometimes we will allow
algorithms that are not deterministic (i.e. they involve one or more random outcomes for some steps).
Notation: At the beginning of an algorithm we will specify the input (a list of the types of values we
are given on which to run the algorithm) and the output (a list of the types of values we will produce
when the algorithm finishes). Each instruction in the algorithm will be written on a separate line, and
will be numbered in the order in which they are listed on the page. The operator ":=" is used to state
that the expression on the right hand side should be evaluated, and then stored in the variable on the
left hand side. We use square bracket notation to indicate a cell in an array. We use the keyword
return to indicate the output of a value and the termination of the algorithm. We use the constructs
if, if...else, for, foreach and while. , and we use either { and }, or indentation to
show compound statements. We assume standard test operators ==, <, <=, >, =>, !=, and !, and the
standard arithmetic operators +, -, *, /.
5
Algorithm 6.4: linear-search
Input: an array x of objects, length n
Input: y, the object we want to find
Output: pos, the position of y in the array, or 0 if it isn't there
1. pos := 0
2. i := 1
2. while (pos == 0 and i <= n)
4. if (x[i] == y)
5. then pos := i
6. else i := i+1
7. return pos
6
7. Graphs and Trees
(Algorithms and structured collections of objects)
Definition 7.5: In a simple graph G = (V, E), two vertices v1 and v2 are adjacent if there is an edge
{v1,v2} ∈E. The edge {v1,v2} is said to be incident on v1 and v2, and v1 and v2 are the endpoints of the
edge. An edge {v} is incident on v twice.
Definition 7.6: In a simple graph G = (V, E), the degree of a vertex, v, is the number of times the
edges in E are incident on v.
Theorem 7.7: The sum of the degrees of all vertices in a simple graph G is equal to twice the number
of edges in G.
Theorem 7.8: (The Handshaking Lemma) The number of vertices with odd degree is even.
Definition 7.9: A complete graph is one where every vertex is adjacent to every other vertex.
Definition 7.10: An orientation of an edge is the selection of one of the vertices as the start point, and
the other edge as the end point. A loop edge has only one orientation.
Definition 7.11: A path in a graph is a sequence of oriented edges, such that the end point of one
oriented edge is the start point of the next oriented edge in the sequence. The length of a path is the
number of edges in the sequence.
Note: a path may be represented as a sequence of oriented edges, or as the sequence of vertices that
the path visits.
7
Definition 7.12: A simple graph is connected if and only if every pair of vertices v and w can be
connected by a path that starts at v and ends at w.
Definition 7.14: A weighted simple graph G is an ordered triple (V, E, c), where
V is a set of vertices
E is a set of edges, where each edge in E is a set of 1 or 2 vertices from V
c is a function c: E → N which assigns a numerical weight to each edge in E.
Definition 7.15: The cost of a path P in a weighted graph is the sum of the weights of all edges in P.
Algorithm 7.16: dijkstra (for finding the cheapest path in a weighted simple graph)
Input: a weighted graph G=(V,E,c), where V ={1,2,...,n}
Input: x, a vertex in V, for the start of the path
Input: y, a vertex in V, for the end of the path
Output: L, a 2d array representation of the path, or null if none
1. L := a 2D array with 3 rows for each of n vertices, all null
2. v := x
3. L[v][2] := 0 //cost of getting to v
4. L[v][3] := 0 //previous vertex in path to v
5. while v is not null
6. for each vertex j adjacent to v //expand paths from v
7. if L[j][1] =/= 1 //if j not done
8. then cost := L[v][2] + c({v,j}) //cost of adding edge v-j
9. if L[j][2]== null OR cost < L[j][2] //if better
10. then L[j][2] := cost //update j's cost
11. L[j][3] := v //say how we got here
12. L[v][1] := 1 //mark v as done
13. v := vertex with smallest L[v][2] and with L[v][1]== null
14. or null if there isn't one //find cheapest vertex
15. if v == y, return L //stop – we've found the cheapest path
16. return null //we didn't reach the target by any path
8
Definition 7.17: A circuit in a graph is a path that starts and ends at the same vertex.
Definition 7.18: An Euler circuit in a graph is a circuit that contains every edge exactly once.
An Euler path in a graph is path that contains every edge exactly once.
Theorem 7.19: A graph has an Euler circuit if and only if every vertex has even degree.
Definition 7.20: A Hamiltonian circuit in a graph is a circuit that contains every vertex exactly once
(apart from the start vertex, which also appears at the end). A Hamiltonian path in a graph is path that
contains every vertex exactly once.
Definition 7.21: Two graphs G1 = (V1,E1) and G2 = (V2,E2) are isomorphic if and only if there is a
bijective function g: V1 → V2 with the property that there is an edge in E1 between vi and vj if and
only if there is an edge in E2 between g(vi) and g(vj).
Definition 7.23: A cycle in a graph is a circuit such that: (i) it contains at least one edge, (ii) no edge
is used twice (in any orientation), and (iii) no vertex except the start vertex appears twice.
Definition 7.25: A rooted tree is a directed simple graph obtained from a tree by selecting one vertex
as the root, and then orienting all edges so that there is a directed path from the root to all other
vertices.
Definition 7.26: Let T be a rooted tree. If there is an edge (x, y) in T, then x is the parent of y and y is
the child of x. If a vertex x is the parent of both y and z then y and z are siblings of each other. A
vertex with no children is a leaf. A vertex with children is an internal vertex. The ancestors of a
vertex x are all the vertices on the path from the root to x. If x is an ancestor of y, then y is a
descendant of x.
Definition 7.28: A spanning tree for a simple graph G=(V,E) is a subgraph of G that contains every
vertex in V.
Theorem 7.29: A simple graph is connected if and only if it has a spanning tree.
9
Algorithm 7.30: prim (for finding a minimal spanning tree in a weighted simple graph)
Input: connected undirected weighted graph G = (V,E,c) with n
vertices
Output: a spanning tree T=(V,F) for G
1. T := {v}, where v is any vertex in V
2. F := { }
3. for each i from 2 to n
4. e := {w,y}, an edge with minimum weight in E such
that w is in T and y is not in T
5. F := F U {e}
6. T := T U {y}
7. return (T,F)
10
8. Counting
(Understanding algorithms and counting)
Definition 8.4: The factorial n! of a number n≥0 is the product of all numbers from n down to 1.
n! = n * (n-1) * (n-2) * ... * 2 * 1
By definition, 0! = 1.
Definition 8.5: A permutation of the elements of a set S is an ordered list of all the elements of S.
Definition 8.7: An r-permutation of the elements of a set S is an ordered list of r of the elements of S.
Theorem 8.8: The number of different r-permutations of a set of size n, denoted nPr , is
n
Pr = n!/(n-r)! = n * (n-1) * (n-2) * ... * (n – r + 1)
and is read as "n P r" or "n perm r".
Theorem 8.9: The number of different subsets of size r of a set of size n, denoted nCr , is
n
Cr = n!/r!(n-r)!
and is read as "n C r" or "n choose r". It is sometimes also denoted by !! .
! !
Theorem 8.10: !
= !!!
Theorem 8.11: The number of different groups of r objects (where we can repeat objects in the
!!!!!
group) selected from a set of n objects is !
.
11
Theorem 8.12:
𝑛 ! ! 𝑛 !!! ! 𝑛 !!! ! 𝑛 𝑛 ! !
(𝑥 + 𝑦)! = 𝑥 𝑦 + 𝑥 𝑦 + 𝑥 𝑦 + ⋯ + 𝑥 !!(!!!) 𝑦 !!! + 𝑥 𝑦
0 1 2 𝑛−1 𝑛
Notation:
!!"!
Theorem 8.13:
!
1
𝑖 = ∗ 𝑛 ∗ (𝑛 + 1)
2
!!!
Theorem 8.14:
!
2! = 2!!! − 1
!!!
Definition 8.15: The logarithm of a number is the power to which the base must be raised to equal the
number. That is, if logb n = k, then bk = n.
Theorem 8.16: If we start with a number n and divide it by 2, and then divide the result by 2, and so
on until we get a result ≤ 1, then we require ceil(log2 n) divisions.
12
9. Algorithms and runtime
(Understanding algorithms and counting)
Note: Big-Oh notation tells that that there is a bound on the size of function f : it never gets
bigger by more than some constant multiple of g applied to the same input. We use Big-Oh
notation for classifying how much work an algorithm has to do on different lengths of input.
Theorem 9.2:
If f : N→N is such that f(x) = anxn + an-1xn-1 + an-2xn-2 + ... + a1x + a0, where each ai is a constant,
then f(x) is O(xn).
Theorem 9.3:
If f : N→N is such that f(x) = anxn + an-1xn-1 + an-2xn-2 + ... + a1x + a0, where each ai is a constant,
then f(x) is not O(xn-1).
Note: Theorems 9.2 and 9.3 mean that if an algorithm does an amount of work that is some
polynomial in the length of the input, then we can classify the algorithm by the highest power
in the polynomial.
Theorem 9.5: Algorithm 6.4 (linear search) has runtime O(n), where n is the length of the input list.
(More precisely, linear search carries out the test in line 4 O(n) times)
Theorem 9.6: Algorithm 6.5 (binary search) has runtime O(log2n), where n is the length of the input
list. (More precisely, binary search carries out the test in line 5 O(log2n)times)
13
Theorem 9.8: Bubble-sort has runtime O(n2) in the worst case, where n is the length of the input list.
(More precisely, bubble-sort carries out the test in line 3, or the swap in line 4 O(n2)times in the worst
case)
Theorem 9.10: Insertion-sort has runtime O(n2) in the worst case, where n is the length of the input
list. (More precisely, insertion-sort carries out the test in line 3 O(n2)times in the worst case)
Definition 9.11: A recursive definition of a function or algorithm is one which specifies the output or
behaviour directly for a base case, and then calls itself (usually on simpler or smaller input) to
compute more complicated cases.
14