Bite-Sized Mathematics for Computer Science

Notes on MIT's 6.042J

Eda Eren
Table of Contents

Introduction

Prerequisites & Resources

Unit 1: Proofs

00. Introduction to Proofs

01. (Two) Proof Methods

02. The Well Ordering Principle

03. Logic & Propositions

04. Quantifiers and Predicate Logic

05. Sets

06. Binary Relations

07. Induction

09. Recursive Definitions

10. Infinite Sets

Unit 2: Structures

11. GCDs

12. Congruences

13. Euler's Theorem

14. RSA Encryption

15. Digraphs: Walks and Paths

16. Directed Acyclic Graphs

17. Partial Orders and Equivalence

18. Degrees & Isomorphism

19. Coloring & Connectivity

20. Trees

21. Stable Matching

Unit 3: Counting

22. Sums & Products

23. Asymptotics

24. Counting with Bijections

25. Repetitions & Binomial Theorem

26. Pigeonhole Principle, Inclusion-Exclusion

Unit 4: Probability

27. Intro to Discrete Probability

28. Conditional Probability

29. Independence & Causality

30. Random Variables, Density Functions

31. Expectation

32. Deviation: Markov & Chebyshev Bounds

33. Sampling & Confidence

34. Random Walks & PageRank

State Machines — Invariants

State machines are abstract models of step-by-step processes.

We can think of a state machine as a binary relation on a set.
In this case, the elements will be called states, the relation will be called transition relation or state graph, and an arrow in the graph will be called a transition.
A transition from state qq to state rr is written as qrq \longrightarrow r.
State machines also have a specific start state.

An example below is one that counts from 0 to 99 and overflows at 100. Not very useful.

State machine example

Figure 5.7 State transitions for the 99-bounded counter.

From the course notes Mathematics for Computer Science.

State machines that have at most one transition out of each state is called deterministic. One example is the one above that counts from 1 to 99, and overflows after that. The only transition is from nn to n+1n + 1.

Note: The general model of a state machine is also about responding to the inputs given, but we are not looking at this for simplicity's sake.


When we used induction the last time, we were showing that a property holds in every step of the process.
A property that is preserved through the whole process is called the preserved invariant.

The invariant principle says that:

If a preserved invariant of a state machine is true for the start state, then it is true for all reachable states.

A reachable state is a state that appears in some execution.
And, what is execution?
It's a sequence of states with the property that:
a) it begins with the start state
b) if qq and rr are consecutive states, then qrq \longrightarrow r

Realize that the invariant principle is just induction, applied to state machines.


There is a technique called fast exponentiation. Take a look at this code:

base = int(input('Enter base: '))
exp = int(input('Enter exponent: '))

def fast_exp(x, y, z):
    if z < 0: return 'Nonnegative integers only!'
    if z == 0: return y
    if z % 2 == 0:
        return fast_exp(x ** 2, y, z // 2)
    if z % 2 == 1: 
        return fast_exp(x ** 2, x * y, z // 2)

print(fast_exp(base, 1, exp))

Notice that we assume z (the exponent) to be an integer as we use integer division (z // 2) like so.

Here, our start state is (base, 1, exp).

Transitions will be according to this rule:

(x, y, z){(x2, y, quotient(z, 2))if z is nonzero and even(x2, xy, quotient(z, 2))if z is nonzero and odd(x, \ y, \ z) \longrightarrow \begin{cases} (x^2, \ y, \ \text{quotient}(z, \ 2)) & \text{if } z \text{ is nonzero and even}\\ (x^2, \ xy, \ \text{quotient}(z, \ 2)) & \text{if } z \text{ is nonzero and odd} \end{cases}

And, our preserved invariant is that zz is a nonnegative integer, and yxz=baseexpyx^z = \text{base}^{\text{exp}}.

One way to verify yxz=baseexpyx^z = \text{base}^{\text{exp}} is to see if the transitioned state is what we think it is.

Since we're only looking at the case when zz is odd, we can use (z1)/2(z - 1) / 2 to do the "integer division," and steer clear of a remainder.

In this transition, yy becomes xyxy, xx becomes x2x^2, and zz becomes (z1)/2(z - 1) / 2.

In order to write yxzyx^z, we have to write (xy)(x2)(z1)/2(xy)(x^2)^{(z - 1) / 2}.

Doing a little simplifying, we get (xy)xz1(xy)x^{z - 1}, and further simplifying we get yxzyx^z which is indeed what we assumed to be baseexp\text{base}^{\text{exp}}!
So, the new values of xx, yy, and zz satisfy the invariant.


To verify a program, there are two properties to look at: partial correctness and termination.

In the fast exponential example, we've proved the correctness, so let's see if we can figure out if it terminates.

One thing to notice is that at each transition, zz becomes quotient(z, 2)\text{quotient}(z, \ 2), so it gets smaller. Since it is a nonnegative integer, there is a point where it can't get any smaller, in fact, it can't get smaller more than log2(exp)log_2(\text{exp}) times, by then it'd already hit 00. So, it indeed terminates.


A derived variable is a function assigning a "value" to each state.

So, take a derived variable vv:

v:StatesValuesv : \text{States} \rightarrow \text{Values}

If the values are natural numbers, it can be said that "vv is N\mathbb{N} valued".