← Back
CSNotes

LANGUAGES AND STRINGS:

Why do we bother to study the theory of computation?

  • The Theory of Computation is the study of the fundamental capabilities and limitations of computers. It’s not just about learning how to code in a specific language, rather, it’s about asking and answering abstract questions such as:
    • “What can and cannot be computed?” are there problems that are IMPOSSIBLE for any computer to solve, no matter how powerful it is?
    • “How efficiently can a problem be solved?” so if a problem CAN be solved, how much memory or time will it take?

So, we learn the fundamental rules and limits of what computers can and can’t do. This knowledge lasts forever, hence why it’s important to study the theory of computation, so if a new programming language emerges, we have some knowledge about its limitations and capabilities

So, you basically start asking yourself “Can my language do this?” and ask “Is this POSSIBLE to do, and if so, what is the best possible performance I can achieve?”

In order to process a program, a computer has to solve several problems, all based on strings of characters. It gets broken down into:

  • Lexical Analysis: Break the code into pieces, basically break them into variables and numbers
  • Parsing: Create a tree that corresponds to the sequence of operations that should be executed
  • Optimization: Make the code faster or simpler
  • Termination: Can we prove the program will ever stop?
  • Interpretation: Actually running the program and see what it does

So, this problem:

int alpha, beta;
alpha = 3;
beta = (2 + 5) / 10;

can be broken down into this tree:

In order to study all these different problems, we need one common way to talk about them. That framework is called Language Recognition

A language is a set of strings over an alphabet

STRINGS:

A string is a finite sequence of symbols drawn from some alphabet. A string can be empty, we define this with ϵ\epsilon (epsilon)

  • an example of a string is something like 001 or cat

An alphabet is a finite, non empty set of symbols. An alphabet is usually defined with ∑\sum. An alphabet CANNOT be infinitely large

  • an example of alphabets can be a binary alphabet, which is just the set ∑={0,1}\sum=\{0,1\}

we denote ∑∗\sum^* as the set of ALL possible strings over an alphabet ∑\sum

  • for example, if our alphabet was ∑={0,1}\sum=\{0,1\}, then

    ∑∗={ϵ,0,1,00,01,10,11,000,001,...}\sum^*=\{\epsilon, 0, 1, 00, 01, 10, 11, 000, 001, ...\}

FUNCTIONS ON STRINGS:

Length: ∣s∣|s| is the number of symbols (characters, letters) in ss

  • for example, ∣ϵ∣=0|\epsilon|=0 or ∣1001101∣=7|1001101|=7, ∣hello∣=5|\text{hello}|=5

#c(s)\\\#_{c}(s) is the number of times that cc occurs in ss

  • for example, #a(abbaaa)\\\#_{a}(abbaaa) is basically saying how many aa’s are there in the sequence abbaaaabbaaa?
  • #a(abbaaa)=4\\\#_{a}(abbaaa)=4

Concatenation: stick two strings together basically

  • example, if x=goodx=\text{good} and y=byey=\text{bye}, then xy=goodbyexy=\text{goodbye}, you just stick them together

usually we denote the concatenation of two strings by xyxy but if you see x.yx.y don’t start shitting your pants they mean the same shit

PROPERTIES:

  • The length of ∣xy∣=∣x∣+∣y∣|xy|=|x|+|y|

    • take the goodbye example: ∣goodbye∣=7|\text{goodbye}|=7 and ∣x∣+∣y∣=∣good∣+∣bye∣=4+3=7|x|+|y|=|\text{good}|+|\text{bye}|=4+3=7
  • The empty string ϵ\epsilon is the identity: xϵ=ϵx=xx\epsilon=\epsilon x= x

    • btw, an identity is basically an element that, when combined with any other element using a specific operation, leaves the other element unchanged. so, when you do 100 + 0 = 100, 0 is the identity here. Or if you do 100 x 1 = 100, 1 is the identity here
  • It’s associative, meaning: ∀s,t,w ((st)w=s(tw))\forall s,t,w \ ((st)w = s(tw))

    • example: let s=go,w=od,w=byes=go, w= od, w=bye. If we do the left side first, we will get

      (st)w=(good)bye=goodbye(st)w =(good)bye=goodbye

      now if we do the right side: s(tw)=go(odbye)=goodbyes(tw)=go(odbye)=goodbye, you get the same thing!

  • They are NOT commutative, so doing x+y=y+xx+y=y+x will not give you the same results in strings

    • example: if s=goods=good and t=byet=bye, if we do s+t=goodbyes+t=goodbye, but if we do

      t+s=byegoodt+s=byegood, they arent the same clearly

Repetition (Power): wiw^i means that the string ww is repeated ii times

  • example: w0=ϵw^0=\epsilon (anything to the power of 0 is the empty string)
  • w3=wwww^3=www
  • (bye)2=byebye(bye)^2=byebye
  • a0b3=bbba^0b^3=bbb

Reverse: For each string ww, wRw^R is just the string written backwards

The reverse of ϵ\epsilon is just ϵ\epsilon

  • if ∣w∣=0|w|=0 then wR=w=ϵw^R=w=\epsilon
  • if ∣w∣≥1|w|\ge1 then:
    • ∃a∈∑(∃u∈∑∗(w=ua))\exists a \in \sum (\exists u \in \sum^*(w=ua)) so, we define wR=auRw^R=au^R

OR IF THE SECOND THING DOESNT MAKE SENSE:

For a long string, take the first character and put it at the end of the reverse of the rest.

  • example: abcR=c+(ab)R=c+b+a=cbaabc^R=c + (ab)^R = c+ b + a = cba

THEOREM: The reverse of two concatenated strings is the reverse of the second string followed by the reverse of the first, (wx)R=xR+wR(wx)^R=x^R+w^R given w,xw,x are strings

Example:

(nametag)R=(tag)R+(name)R=gateman(nametag)^R=(tag)^R+(name)^R=gateman

(remember to come back here because there’s the whole proof by induction thing but i need to look more into it)

RELATIONS ON STRINGS:

Substring: A string that appears consecutively inside another string

For example:

  • aaaaaa IS a substring of the string aaabbaaaaaabbaaa
  • aaaaaaaaaaaa IS NOT a substring of the string aaabbbaaaaaabbbaaa (there are no consecutive aaaaaaaaaaaa’s inside the string)

EVERY STRING IS A SUBSTRING OF ITSELF

ϵ\epsilon is a substring of every string

What is the difference between a substring and a proper substring?

  • a substring is any consecutive sequence of characters taken from within the string, this includes:
    • the string ITSELF
    • the empty string
    • any consecutive sequence at the beginning, middle, or end
    • example: for the string s="abc"s="abc", all of its substrings are:
      • ϵ,a,b,c,ab,bc,abc\epsilon,a,b,c,ab,bc,abc
  • a proper substring is any substring that DOES NOT INCLUDE THE STRING ITSELF
    • example: for the string s="abc"s="abc", all of the proper substrings include:
      • ϵ,a,b,c,ab,bc\epsilon,a,b,c,ab,bc

Prefix: A string you can put at the beginning of another string to build it

ss is a prefix of tt if you can add something (x)(x) to the end of ss to get tt (t=s+x)(t=s+x)

EVERY STRING IS A PREFIX OF ITSELF

ϵ\epsilon is a prefix of every string

What is the difference between a prefix and a proper prefix?

  • a prefix is the consecutive sequence of characters staring from left to right that INCLUDES the string
    • example: for the string s="abba"s="abba", the prefixes of abbaabba are:
      • ϵ,a,ab,abb,abba\epsilon,a,ab,abb,abba
  • a proper prefix is basically all prefixes EXCEPT FOR THE ENTIRE STRING
    • example: for the string s="abba"s="abba", the proper prefixes are:
      • ϵ,a,ab,abb\epsilon, a, ab,abb

Suffix: A string you can put at the end of another string to build it

ss is a suffix of tt if you can add something (x)(x) to the start of ss to get tt (t=x+s)(t=x+s)

EVERY STRING IS A SUFFIX OF ITSELF

ϵ\epsilon is a suffix of every string

What is the difference between a suffix and a proper suffix?

  • a suffix is the consecutive characters starting from right to left that INCLUDES the string
    • example: for the string s="abba"s="abba", the suffixes are:
      • ϵ,a,ba,bba,abba\epsilon,a,ba,bba,abba
  • a proper suffix is basically all suffixes EXCEPT FOR THE ENTIRE STRING
    • example: for the string s="abba"s="abba", the proper suffixes are:
      • ϵ,a,ba,bba\epsilon,a,ba,bba

DEFINING A LANGUAGE:

A language is a (finite or infinite) set of string over a finite alphabet ∑\sum

Examples: Let ∑={a,b}\sum=\{a,b\}

Some languages over ∑\sum include:

  • ∅,{ϵ},{a,b},{ϵ,a,aa,aaa,aaaaaa}\emptyset, \{\epsilon\}, \{a,b\}, \{\epsilon, a, aa, aaa, aaaaaa\}, and many more!

The language ∑∗\sum^* contains an infinite number of string, including: ϵ,a,b,ab,ababaa,...\epsilon, a, b, ab, ababaa,...

It is important to know that ∅≠ϵ\emptyset\neq\epsilon, ϵ\epsilon HAS 1 string, but it just happens to have no letters, its just empty

L⊆∑∗L\subseteq\sum^*

If we say that a∈∑a\in\sum, this would mean the contents of ∑\sum would be letters or symbols

If we say that a∈∑∗a\in \sum^*, this would mean the contents of ∑\sum would be STRINGS

  • so, ∑={a,b}\sum=\{a,b\}, this means the contents are called letters
  • ∑∗={ϵ,a,b,aa,ab,ba,bb,aaa,aab...}\sum^*=\{\epsilon, a, b, aa, ab, ba, bb, aaa, aab...\}, there are all STRINGS

Some examples to make stuff more clearer:

L={x∈{a,b}∗∣ all a′s precede all b′s}L=\{x\in\{a,b\}^* | \text{ all }a's\text{ precede all }b's\}

So:

  • ϵ,a,aa,aabbb,bb\epsilon,a,aa,aabbb,bb ARE IN L. While bb may seem like it wouldnt be there, since there are no a’s in the string, you are allowed to just put b’s, the string just happens to have zero a’s which is fine, there is no restriction on saying there HAS to be an a
  • aba,ba,abcaba,ba,abc are NOT in L. in the first and second one, we have an a after a b, and in the last one we have a c which is not even supposed to be there? who invited the horse
  • ϵ,a,aa,bb\epsilon,a,aa,bb ARE IN L.

L={x∈{a,b}∗∣∃y∈{a,b}∗∣x=ya}L=\{x\in\{a,b\}^* | \exists y \in \{a,b\}^* | x=ya\}

So:

  • To translate this into simpler terms, this is essentially saying that the string xx HAS to end with an aa
  • So, ϵ,ba,bba,aaa\epsilon, ba, bba, aaa ARE IN L
  • a,bbab,abba,abba, bbab, abba, abb are NOT in L

What are the following languages?

L={w∈{a,b}∗∣ no prefix of w contains b}L=\{w\in\{a,b\}^* |\text{ no prefix of w contains }b\}

  • this can be: ϵ,a,aa,aaa,aaaa,...\epsilon,a,aa,aaa,aaaa,...

L={w∈{a,b}∗∣ ny prefix of w starts with an a}L=\{w\in\{a,b\}^*|\text{ ny prefix of w starts with an }a\}

  • this can be: ϵ,b,bba,bbba,bbbaa\epsilon, b, bba, bbba, bbbaa

L={w∈{a,b}∗∣ every prefix of w starts with a}L=\{w\in \{a,b\}^*|\text{ every prefix of w starts with }a\}

  • this one is a bit tricky. ϵ\epsilon is a prefix of every string, and ϵ\epsilon does NOT start with aa. So, this means L=∅L=\emptyset

You can use repetition in a language definition:

L={an∣n≥0}L=\{a^n|n\ge0\}

  • ϵ,a,aa,aaa,aaaa,aaaaa,...\epsilon, a, aa, aaa, aaaa, aaaaa,...

LANGUAGES ARE SETS:

There are two main ways to computationally handle a language

  • Generator (enumerator): A machine that LISTS OUT all the strings in the language, one by one
  • Recognizer: A machine that takes a string as input and answers “yes” if string in language, if not, then the answer would be a “no”. obviously, this would be more useful

ENUMERATION:

How do they list strings?

The usually list them in lexicographic order (dictionary order essentially, shortest first)

  • ∑={a,b}\sum=\{a,b\}, ∑={ϵ,a,b,aa,ab,bb,bb,aaa,...}\sum= \{\epsilon, a, b, aa, ab, bb, bb, aaa,...\}

Example, the lexicographic enumeration of:

{w∈{a,b}∗∣ w is even}\{w\in \{a,b\}^* |\ w\text{ is even}\} would be:

  • {ϵ,aa,ab,bb,aaaa,aaab,....}\{\epsilon, aa, ab, bb, aaaa, aaab,....\}

HOW LARGE IS A LANGUAGE:

We know that the smallest possible language is the empty set ∅\emptyset, which has ZERO strings

We also know that the largest possible language is ∑∗\sum^*, which contains every possivle string, which would make that language infinitely large

There is a theory that states:

If ∑≠∅\sum\ne\emptyset, then ∑∗\sum^* is countably infinite

Now, what the HELL does this mean?

  • Let us say you have an alphabet with just two letters {a,b}\{a,b\}, this includes:
  • ϵ\epsilon
  • All 1-letter words (cause of enumeration) {a,b}\{a,b\}
  • All 2-letter words (also, cause of enumeration) {aa,ab,ba,bb}\{aa,ab,ba,bb\}
  • All 3-letter words, and so on, FOREVER!

But, this seems impossible to count, because there is no end! the trick here is to create a list that will eventually include every possible word

So, we sort the words by LENGTH first. Then, in each list, list them in alphabetical order

It would look like:

  • Length 0: ϵ\epsilon
  • Length 1: a,ba,b
  • Length 2: aa,ab,ba,bbaa,ab,ba,bb
  • Length 3: aaa,aab,aba,abb,baa,bab,bba,bbbaaa,aab,aba,abb,baa,bab,bba,bbb
  • Length 4, you get the gist, and so on

Now, why is this list countably infinite?

  • It’s a list: meaning you can number EVERY item. The first word is number 1, the epsilon , the second word is number 2 (a), the third is number 3 (b), and so on

Due to this sorting rule, every single possible string will eventually appear in the list and get assigned its own unique number. Since you can pair each string with a unique number, the set of all strings is the same size as the set of natural numbers, which makes it countably infinite

countably infinite just means you can make an infinite list of something

  • every single item in the set can be given a unique number, and every number on your list will have an item

Another theorem states:

If ∑≠∅\sum\ne\emptyset then the set of languages over ∑\sum is uncountably infinite

Uncountably infinite means there are so many things that is impossible to make a list of all of them. Even an infinite list would miss something

Example:

Think of a 1-centimeter line segment. It begins at 0cm and ends at 1cm

Now, think about how many points are on that line. A point is a specific, exact location, like 0.5cm or 0.5000000000001cm

The number of points between 0 and 1 is uncountably infinite. The reason why you can’t list them is because:

Let us say you try to make a list of EVERY point. It might start like:

  • 1: 0.1cm
  • 2: 0.01cm
  • 3: 0.001cm
  • 4: 0.00001cm
  • you plan to list… quite a lot

You will never, never list the point 0.5cm because you’re listing numbers that just get infinitely closer and smaller towards zero.

So, the set of all points on a line is uncountably infinite. There is no possible way to put them in order and assign each one a number. There are simply too many

All in all, uncountably infinite means that a set is so vast that its impossible to create a complete, numbered list of all elements, making it a larger type of infinite than the infinity of whole numbers (woah..)

DIAGONALIZATION

We know that integers and rational numbers are countable, however, irrational numbers are uncountable.

Proof:

  1. Suppose the irrational numbers WERE countable, we could list them as:

    n1,n2,n3,...n_1,n_2,n_3,...

  2. Construct a new number N by ensuring:

    Its 1st decimal digit ≠ the 1st decimal digit of n1n_1

    Its 2nd decimal digit ≠ the 2nd decimal digit of n2n_2

    Its 3rd decimal digit ≠ the 3rd decimal digit of n3n_3

    …and so on

  3. By construction, NN differs from every nin_i in at least one decimal place

  4. So, NN is NOT on the list, which leads to a contradiction

  5. Therefore, irrationals cannot be listed (they are uncountable)

Let me give a more solid example with numbers so it makes sense:

FUNCTIONS ON LANGUAGES:

Since languages are sets, we can use set operations on them:

  • Union
  • Intersection
  • Complement

We also have special language operations:

  • Concatenation
  • Kleene Star

CONCATENATION:

Concatenation is NOT communitive (a cat b != b cat a all the time)

If L1L_1 and L2L_2 are languages over ∑\sum, then:

L1L2={st ∣ s∈L1 and t∈L2}L_1L_2=\{st\ |\ s\in L_1\text{ and } t\in L_2\}

So, combine every string in the first language with every string in the second

Example:

L1={cat,dog}, L2={apple,pear}L_1=\{cat,dog\}, \ L_2=\{apple,pear\}

L1L2={catapple,catpear,dogapple,dogpear}L_1L_2=\{catapple, catpear, dogapple, dogpear\}

The language {ϵ}\{\epsilon\} is like the number 1 for multiplication. Concatenating anything with {ϵ}\{\epsilon\} gives you the original thing back

L{ϵ}={ϵ}L=LL\{\epsilon\}=\{\epsilon\}L=L

The empty language ∅\emptyset is like the number 0 for multiplication. Concatenating anything with ∅\emptyset gives you ∅\emptyset

L{∅}={∅}L=∅L\{\emptyset\}=\{\emptyset\}L=\emptyset

When defining languages with variables like nn and mm in anbma^nb^m, the variables are independent

For example:

We are given L1={an∣n≥0}L_1=\{a^n | n\ge 0\} and L2={bn∣n≥0}L_2=\{b^n | n\ge 0\}

Concatenating these two means: take one string from L1L_1 and put it directly in front of one string from L2L_2

So:

L1L2={anbm∣n,m≥0}L_1L_2=\{a^nb^m | n,m\ge0\}

example:

  • n=0,m=0→ϵn=0,m=0 \rightarrow \epsilon
  • n=3,m=0→aaan=3, m=0\rightarrow aaa
  • n=0,m=3→bbbn=0,m=3\rightarrow bbb
  • n=2,m=4→aabbbbn=2,m=4\rightarrow aabbbb

L1L2≠{anbn∣n≥0}L_1L_2\ne\{a^nb^n | n\ge 0\}, obviously, they are both independent

KLEENE STAR:

The Kleene star (L∗)(L^*) operation basically means “zero or more concatenations of strings from LL”

This always includes ϵ\epsilon

It includes every string that can be formed by concatenating any finite number of strings from LL

Example:

L={dog,cat,fish}L=\{dog,cat,fish\}

L∗={ϵ,dog,cat,fish,dogdog,catcat,fishfish,dogcat,fishcatfish,...}L^*=\{\epsilon, dog, cat, fish, dogdog, catcat, fishfish, dogcat, fishcatfish, ...\}

PLUS OPERATOR:

The plus operation means “one or more concatenations of strings from LL”

L+=LL∗L^+=LL^*

L+=L∗−{ϵ}L^+=L^*-\{\epsilon\} iff ϵ∉L\epsilon\not\in L. If ϵ∈L\epsilon\in L, then L+L^+ still contains ϵ\epsilon because you could choose it from one of the concatenations

When we say “L+L^+ is the closure of LL under concatenation”, this means:

  • L+L^+ contains all strings that can be formed by concatenating one or more strings from LL
  • It’s the smallest set with that property

CONCATENATION AND REVERSE OF LANGUAGES

(L1L2)R=L2RL1R(L_1L_2)^R = L_2^RL_1^R

The reverse of the concatenation of two languages is the concatenation of their reverses, but in the opposite order

Proof:

We know that (xy)R=yRxR(xy)^R=y^Rx^R

  1. (L1L2)R(L_1L_2)^R is the set of all reversed strings (xy)R(xy)^R where x∈L1x\in L_1 and y∈L2y\in L_2
  2. This equals the set: {yRxR∣x∈L1,y∈L2}\{y^Rx^R | x\in L_1,y\in L_2\}
  3. This set is exactly L2RL1RL_2^RL_1^R

Example:

Suppose L1={a,ab}L_1=\{a,ab\} and L2={b,ba}L_2=\{b,ba\} over the alphabet ∑={a,b}\sum=\{a,b\}

Let us calculate (L1L2)R(L_1L_2)^R

First, find the concatenation of the two languages

L1L2={ab,aba,abb,abba}L_1L_2=\{ab,aba,abb,abba\}

Now, let us reverse this new language:

ab→ba,aba→aba,abb→bba,abba→abbaab\rightarrow ba, aba\rightarrow aba, abb\rightarrow bba, abba \rightarrow abba

So, (L1L2)R={ba,aba,bba,abba}(L_1L_2)^R=\{ba,aba,bba,abba\}

Let us calculate L2RL1RL_2^RL_1^R

L2R={b,ab},L1R={a,ba}L_2^R=\{b,ab\}, L_1^R=\{a,ba\}

Now, concatenate the two:

L2RL1R={ba,bba,aba,abba}L_2^RL_1^R=\{ba,bba,aba,abba\}

They are the exact same!

SEMANTIC VS SYNTAX:

The distinction between the form of a string (syntax) and its meaning (semantics)

For example, the language AnBn={anbn∣n≥0}A^nB^n=\{a^nb^n| n\ge 0\}. While this language is well defined syntactically, what do these strings mean semantic wise? On their own, they are just patterns

Syntax: The formal structure, rules, and patterns of strings in a language

Semantics: The meaning assigned to those strings

DECISION PROBLEMS:

A decision problem is any problem that has a yes/no answer

A decision procedure is an algorithm that solves a decision problem (always halts with the correct answer)

For example:

  • An decision problem could be “is integer nn a prime” and the decision procedure can be some algorithm that checks if nn is prime or not

Our main focus will be:

The language recognition problem: Given a language LL and a string ww, is w∈L?w\in L?

ENCODING:

Problems that don’t look like decision problems can be recast into new problems that do look like it. EVERYTHING is a string

Example:

“Does a program always halt?” can be recast as a decision problem, which would look like: “Given a program pp, written in some standard programming language, is pp guaranteed to halt on all inputs?”

The language to be decided:

HPALL={p∣p halts on all inputs}HP_{ALL}=\{p | p\text{ halts on all inputs}\}, obviously, pp halts IFF p∈HPALLp\in HP_{ALL}

Convention:

<X> represents the string encoding of the object X

<X,Y> represents the string encoding of the pair objects X and Y

Another example:
You can transform a function computation problem into a verification problem

Original problem: Compute the product of two integers

Recast as decision: Instead of computing x * y, verify if a given answer z is correct

Language to be decided: L={w of the form: x×y=z∣z is any well formed integer, and z=x×y}L=\{w\text{ of the form: }x\times y = z|z \text{ is any well formed integer, and }z =x \times y\}

so it would basically be:
L = { x * y = z such that integer_3 = int_1 x int_2 }. if int 1 times int 2 is not int 3, then it isn't in the language

  • so 12×9=108∈L12\times 9=108\in L, but 12×8=108∉L12\times 8=108\not\in L

Languages and machine

  • SD: Semi-Decidable (Countable infinite)

  • D: Decidable Languages (Countable infinite)

  • Context-Free Languages:

    • FSMs: Finite State Machines (Finite Automata)
    • All Compilers are here
  • Regular Languages:

    • PDAs: Pushdown Automata(s)

Claim: Everything outside SD is impossible to say anything about it (Will be proved later)

Rule of Least Power:

  • This is the way to tackle each problem in the course? (I guess)

Turing machines

  • Turing machines have a tape that they can read and write stuff on by moving the head pointer (the arrow in this picture)

Grammars, Languages, and Machines

Examples of each

  • Grammar: Generate any possible code in C++
  • Machine (Compiler): Get that code and tell you if it is syntactically correct.
  • Language: Creates the strings used throughout the program (fact check cause idk)