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

08. State Machines — Invariants

09. Recursive Definitions

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

Infinite Sets

Dealing with infinity is not what you expect while studying discrete math. But, we've been working with infinite sets all along — using integers, rationals, etc., so let's look at them a bit more in depth.

One obvious point is one difference between an infinite and a finite set. In a finite set, adding an element makes the set bigger, but that's not true for infinite sets.
Let's say AA is a finite set, and bAb \notin A.
Then, A{b}=A+1|A \cup \{b\}| = |A| + 1.
We can see that A|A| (the size of AA) and A{b}|A \cup \{b\}| (the size of A with bb) are not the same.

However, now let's say that AA is an infinite set, and bAb \notin A. In this case, we can only say that AA is infinite iff there is bijection between A|A| and A{b}|A \cup \{b\}|.
So, if we have an infinite set, and we add an element to it, we still have an infinite set.

But, there is an important idea that there are different sizes of infinities (you might already be familiar with it if you've heard of Cantor before).

Following that idea, we can also differentiate finite sets and infinite sets when it comes to relations. Now, when we think of a surjective relation between two finite sets (A surj BA \text{ surj } B), we can think of it differently when it comes to infinite sets, now it will mean that the set AA is larger or equal to the set BB, that is, AB|A| \geq |B|.
Likewise, when talking about a bijective relation (A bij BA \text{ bij } B), when it comes to infinite sets, it will mean that both infinite sets have the same sizes (A=B|A| = |B|) — so, two infinite sets have the same size when there is a bijection between them.

There is also a related idea, the Schroeder-Bernstein Theorem. It says that if set AA is at least as big as set BB, and conversely if set BB is at least as big as set AA, then set AA is the same size as set BB. (if (A surj BA \text{ surj } B and B surj AB \text{ surj }A), then (A bij BA \text{ bij }B).)

If English is slowing down your imaginative capabilities, here is the thing put precisely:
ABA    A=B|A| \geq |B| \geq |A| \implies |A| = |B|


Remember that a notation like AA^* is used when it comes to the set of finite bit strings {0, 1}\{0, \ 1\}^*. Now, if that set is infinite, we'll denote it like AωA^\omega, the infinite set of bit strings {0, 1}ω\{0, \ 1\}^\omega.


A countable set is a set where you can list the elements as a0, a1, a2,...a_0, \ a_1, \ a_2, ....
So, let's say we have a countable set AA; we can write down its elements like a0, a1, a2,...a_0, \ a_1, \ a_2, ... and, so on. It means that we can create a mapping between the elements and the set N\mathbb{N} — what follows from that is there is a bijection between N\mathbb{N} and our countable set AA (N bij A\mathbb{N} \text{ bij }A). In that case, AA is countably infinite. But, finite sets can also be countable.

Also, N surj AN \text{ surj } A implies that AA is countable as well — remember that it means that the range of AA is its whole codomain, and that means all the elements are hit by at least one arrow from NN.

Then, what about uncountable sets?
We can start with an example. Cantor's diagonal argument shows that the set of infinite binary sequences {0, 1}ω\{0, \ 1\}^\omega is uncountable.

We can think of "a list" of infinite binary sequences. However, using each element diagonally, we can create another new infinite binary sequence, like this:
An example of Cantor's diagonal argument

In that case, the set of natural numbers is strictly smaller than set AA, that is, N<A\mathbb{N} \lt A, as there is no surjection from the set of natural numbers to AA. That is, there are elements in AA that are not hit by arrows coming from the set of natural numbers.


The Halting Problem Basics

Let's take a look at the Halting Problem that's briefly touched upon in this course:

The general Halting Problem for some programming language is, given an arbitrary program, to determine whether the program will run forever if it is not interrupted.

The thing we're looking for is a way of detecting that a program does not halt.

Let's say we have a string s that defines a procedure (a function) P.
We are going to write very crude pseudocode through this example.

So, s is something like this:

s = '''
def P(f):
    # do stuff (return or not return, that is the question)
'''

Now, s is said to halt iff P(s) returns (when a function returns, obviously it halts). Notice that what we're doing is passing s itself to what is defined by s.

Let's assume that there is a function Q that decides whether s halts. We are arguing that it is impossible to exist, but for the sake of contradiction, let's say it does:

# THIS CANNOT EXIST
def Q(s):
    If s halts:
        return True
    Else:
        return False

So, Q(s) returns true if s halts, and false otherwise. It is a "halts decider".

We are now going to have another function Q_PRIME that is kind of a modified version of Q.

def Q_PRIME(s):
    If !Q(s): # (that means s does not halt)
        return True
    Else:
        go on forever # (that means s halts)

So, Q_PRIME(s) returns true if Q(s) returns false, and it returns nothing (it doesn't halt) if Q(s) returns true.

Again, if Q_PRIME(s) returns nothing, that means s halts (because Q_PRIME(s) returns nothing when Q(s) returns true. And, Q(s) returns true when s halts).

Now, just like s defining a function P, let's say that t defines the function Q_PRIME itself.

So, t is said to halt iff Q_PRIME returns (when a function returns, obviously it halts).

Here is the thing, though: When we pass Q_PRIME the string defining itself (t), there is a contradiction.
Q_PRIME(t) returns iff t doesn't halt (because Q(t) returns false). That is, if Q_PRIME doesn't halt, it returns — this is a contradiction.
So, t halts iff t doesn't halt.

In short, if Q_PRIME(t) returns, that means it halts. But, it can only return if it doesn't halt, that is, if Q(t) returns false, because that's how we defined it.

When applied to itself, we reach a contradiction.
And, therefore there cannot be such Q, a "halts decider".
It is undecidable.


Self-referentiality is more than an interesting idea. It has an important role in set theory, computer science, and the problem of consciousness as well.
For our purposes, self-application, the idea of taking a function and applying it to itself, is important.
There are some classic examples like

This sentence is false.\text{This sentence is false.}

If it's false, then it has to be true; but when it's true, it means that it's false. That's a paradox.

But, self-application is taken for granted in computer science. Take a look at the simple trivial example like the pow function that returns the result of taking xx to the power of yy:

from math import pow

pow(pow(2, 3), 2) # 64.0
pow(pow(pow(2, 3), 2), 4) # 16777216.0

Russell's Paradox

What Russell was proposing was this:
W={sSets  ss}W = \{s \in \text{Sets} \ | \ s \notin s\}

So, WW is a collection of sets ss such that ss is not a member of ss; a collection of sets that are not members of themselves.

It implies that sW iff sss \in W \text{ iff }s \notin s.
Now, if we let ss be WW, that's a contradiction: WW iff WWW \in W \text{ iff }W \notin W.

But, there is a fix: we can avoid this paradox if we don't allow WW to be a set.
That's okay, but it raises another philosophical question: when is a set a set, and when is it not a set?

ZFC (Zermelo-Frankel set theory with Choice), which provides an axiomatic system for set theory addresses this issue of self-referentiality, and states that no set can be a member of itself:

Also, all sets are well-founded under membership:
xy[z zx    zy]x \in y \land [\forall z \ z \in x \implies z \notin y]
Meaning that xx is built out of things that are not in yy.
It's also said that x is ϵ minimal in yx \text{ is } \epsilon \text{ minimal in } y.
Remember the well-ordering principle? Every nonempty set of nonnegative integers has to have a least element, in other words, it can't be decreasing infinitely. It is a similar idea: you can't have an infinite sequence of sets where each of which is a member of the previous one.