O principal objetivo desta disciplina é explorar a questão
Quais são as capacidades e limitações dos computadores?
Outras questões que surgem ao longo dessa disciplina são:
Durante os anos de 1930-1950, matemáticos estavam preocupados em responder a questão “o que é computável?”.
Nesse período foram criadas as bases para o que hoje chamamos de Ciência da Computação. Foram estudados problemas como verificação de programas e a verificação da veracidade matemática (que não são computáveis), e foram criados modelos computacionais, como os autômatos finitos e a Máquina de Turing, na tentativa de formalizar o problema.
Desde os anos 1960, o principal problema que tentamos responder é “o que é computável na prática?”, onde obtemos formas de medir o tempo ou o espaço necessário para se chegar a uma solução de um problema que sabemos ser computável. Utilizando os mesmos formalismos, tentamos entender por que alguns problemas podem ser resolvidos de forma eficiente (como a ordenação de elementos) e outros são tão complexos que se tornam intratáveis (como o problema do Caixeiro Viajante).
Entender porque um problema é solucionável, e se existe uma forma eficiente de resolvê-lo é muito importante, e, até hoje, não sabemos se alguns problemas simplesmente não tem resposta, ou se não sabemos o suficiente para encontrar uma resposta.
"We have a very limited knowledge of computation."
(Michael Sipser, 2020)
Podemos classificar os problemas computacionais em três tipos básicos: os de fácil solução, os de difícil solução, e os insolucionáveis. Problemas como a ordenação de elementos, ou a procura de rotas entre dois pontos são fáceis de resolver. Problemas como o escalonamento com restrições são difíceis de solucionar. Alguns problemas simplesmente não podem ser resolvidos com um computador.
A Teoria da Complexidade busca resposta para uma pergunta que intriga os cientistas da computação há muito tempo, o que faz alguns problemas computacionalmente difíceis e outros fáceis? Esta, ainda hoje, é uma pergunta sem resposta.
Uma aplicação muito interessantes da teoria da complexidade é a criptografia. Uma vez que na maioria dos sistemas procuramos obter formas mais rápidas de solucionar um problema, no campo da criptografia, o objetivo é encontrar um problema de difícil solução para quem não conheça a chave criptográfica, para que seja virtualmente impossível quebrar o código secreto.
Durante a primeira metade do século XX, matemáticos com Kurt Gödel, Alan Turing e Alonzo Chuch descobriram que certos problemas básicos não podem ser resolvidos por computadores. Entre as conseqüências desse resultado estava o desenvolvimento dos modelos teóricos de computadores, que ajudariam na construção de computadores reais.
As teorias de complexidade e computabilidade estão intimamente relacionadas. Na teoria da complexidade o objetivo é classificar os problemas como fáceis e difíceis, enquanto na teoria da computabilidade, a classificação dos problemas é entre os problemas que são solucionáveis e os que não o são. A teoria da computabilidade introduz vários dos conceitos usados na teoria da complexidade.
A teoria dos autômatos no auxilia na definição de modelos matemáticos de computação. Um desses modelos, o autômato finito, é utilizado em processamento de textos, compiladores e projetos de hardware. Um outro modelo, a gramática livre de contexto é utilizada na definição de linguagens de programação e em inteligência artificial.
A relativa simplicidade dos modelos apresentados pela teoria dos autômatos é excelente para se começar a estudar a teoria da computação. As teorias da computabilidade e da complexidade requerem uma definição precisa de um computador, e o teoria dos autômatos permite praticar com definições formais de computação.
Veja a página sobre Conjuntos
Veja a página sobre lógica booleana
Uma ótima introdução a grafos pode ser encontrada no Cap. 09 do livro Fundamentals of Computer Science
Veja a página conceitos sobre Grafos.
Uma sequência é uma lista de objetos em uma ordem arbitrária. Em geral, descrevemos a lista entre parênteses:
\[(73, 11, 17)\]Em um conjunto, a ordem e a repetição de objetos não são relevantes, mas em uma sequência sim. A lista $(14, 93, 22)$ não é igual a lista $(93, 14, 22)$, nem a lista $(a, b, c)$ é igual a lista $(a, a, b, b, b, c)$.
Como no casos dos conjuntos, sequências poder ser finitas ou infinitas. As sequências finitas são frequentemente chamadas de uplas. Uma sequência com $k$ elementos é uma k-upla, logo, a sequência $(1, 2, 3)$ é uma 3-upla, e a sequência $(a, b)$ uma 2-upla. Uma 2-upla é normalmente chamada de par.
Sequências e conjuntos não precisam ser homogêneos, podendo conte qualquer tipo de objeto, como em
\[\{1, 5, 8, \text{"Hello"}\}\]Sequências e conjuntos são objetos como qualquer outro, logo, podemos ter sequências de sequências, sequências de conjuntos, conjuntos de sequências, ou conjuntos de conjuntos (como o conjunto das partes (powerset).
Note que a definição de uma sequência pode ser utilizada para demonstrar que o produto cartesiano não é uma operação comutativa ou associativa, uma vez que o resultado do produto são sequências.
Veja a página Funções e Relações.
Um alfabeto é um conjunto não-vazio e finito de símbolos. Utilizamos as letras gregas $\Sigma$ ou $\Gamma$ para referenciar alfabetos. São exemplos de alfabetos:
\[\begin{align} \Sigma_1 &= \{0, 1\} \\ \Sigma_2 &= \{a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z\} \\ \Gamma &= \{1, 2, 3, x, y, z\} \\ \end{align}\]Uma string (ou cadeia) sobre um alfabeto, é uma sequência finitas de símbolos daquele alfabeto, escritos de forma sequencial, por exemplo $0011010$ é uma cadeia sobre $\Sigma_1$. Cadeias de símbolos são blocos básicos fundamentais em ciência da computação.
Se $w$ é uma cadeia soblre um alfabeto $\Sigma$, o comprimento de $w$, representado por $|w|$ é o número de símbolos que a cadeia contém. A cadeia de comprimento zero ($|w| = 0)$ é chamada de cadeia vazia (ou string vazia) e é representada por $\varepsilon$. A string vazia desempenha o papel do 0 em um sistema numérico.
Seja $w$ uma string de comprimento $n$, sobre o alfabeto $\Sigma$, podemos escrever $w = w_1w_2w_3{\cdots}w_n$ onde $w_i \in \Sigma$. O reverso de $w$, representado por $w^{\mathcal{R}}$, é obtido escrevendo-se $w$ na ordem inversa, ou seja, $w^\mathcal{R} = w_{n}w_{n-1}{\cdots}w_3w_2w_1$.
Uma subcadeia $z$ é uma subcadeeia de $w$ se $z$ aparece consecutivamente dentro de $w$, por exemplo $cada$ é uma subcadeia de $abracadabra$, mas não é uma subcadeia de $cadeia$.
Dada uma cadeia $x$ de comprimento $m$ e uma cadeia $y$ de comprimento $n$ a concatenação de $x$ e $y$, escrita como $xy$, é a cadeia obtida concatenando-se $y$ ao final de $x$, com em $x_1{\cdots}x_my_1{\cdots}y_m$. Ao concatenar uma cadeia com ela mesma, podemos utilizar a notação com expoente:
\[x^k = \overbrace{xx{\cdots}x}^{k}\]A ordenação lexicográfica de cadeias é a mesma ordenação que encontramos no dicionário. Porém, em linguagens formais, utilizamos uma variação da ordenação lexicográfica, onde as cadeias mais curtas sempre precedem as mais longas. Esta ordenação é conhecida por shortlex ou radix. Apesar disso, nos textos da disciplina os três termos podem ser utilizados significando o mesmo tipo de ordenação, por exemplo, a ordenação lexicográfica de todas as cadeias sobre o alfabeto $\Sigma = {0,1}$ é:
\[(\varepsilon, 0, 1, 00, 01, 10, 11, 000, \dots)\]Uma linguagem é um conjunto de cadeias.
ada
0.3
e 0.4
.