Trilha 18 · Lógica e teoria da computação

Máquina de Turing

A Máquina de Turing (Alan Turing, 1936) é o modelo teórico mais poderoso de computação: uma fita infinita, uma cabeça de leitura/escrita e uma tabela de transições. Toda linguagem de programação moderna é equivalente a ela. O problema da parada prova que algumas perguntas nunca terão resposta algorítmica.

① Intuição

Uma fita infinita e uma cabeça que lê, escreve e se move

Imagine uma fita dividida em células, cada uma contendo um símbolo. Uma cabeça de leitura/escrita aponta para uma célula e, baseando-se no estado atual e no símbolo que lê, decide: qual símbolo escrever, para qual lado mover a cabeça e para qual estado ir.

Esse modelo absurdamente simples — descrito em 1936, antes de existir qualquer computador eletrônico — captura toda a computação possível. Qualquer algoritmo que você escreve em Python pode ser traduzido para uma Máquina de Turing equivalente, e vice-versa.

Turing-completude: dizemos que um sistema é Turing-completo quando pode simular qualquer Máquina de Turing. Python, JavaScript, SQL (com CTEs recursivos), até o CSS com animações e contadores — e a linguagem de células do Excel — são Turing-completos. Até o jogo Minecraft foi usado para implementar um computador de 8 bits em redstone.
② Visualização interativa

TM de incremento binário

A máquina percorre a fita até o fim, depois volta incrementando da direita: 1→0 com carry, para em 0→1. Tente "0111" (→"1000") ou "111" (overflow → "1000").

entrada binária:
FITA (infinita)
B
0
1
1
1
B
B
B
B
B
B
B
B
q₀
pos 1 · símbolo: 0
Início: fita carregada com "0111". q₀ vai varrer para a direita até encontrar o final.
1/10
③ Explicação técnica

Simulando uma Máquina de Turing

# Máquina de Turing — definição formal e simulação
# TM = (Q, Σ, Γ, δ, q₀, B, F)
#   Q: estados finitos
#   Σ: alfabeto de entrada
#   Γ: alfabeto da fita (Σ ⊆ Γ, inclui B = branco)
#   δ: Q × Γ → Q × Γ × {L, R} (transição)
#   q₀: estado inicial
#   B: símbolo branco
#   F: estados de aceitação

# TM para incremento binário: "0111" → "1000"
delta = {
    # (estado_atual, símbolo_lido): (próx_estado, símbolo_escrito, direção)
    ("q0", "0"): ("q0", "0", "R"),   # q0: varre direita
    ("q0", "1"): ("q0", "1", "R"),
    ("q0", "B"): ("q1", "B", "L"),   # chegou ao fim → vai esquerda
    ("q1", "1"): ("q1", "0", "L"),   # q1: flip 1→0, carry
    ("q1", "0"): ("qf", "1", "R"),   # absorve carry
    ("q1", "B"): ("qf", "1", "R"),   # overflow: todos eram 1
}

def run_tm(tape_input):
    tape  = list("B" + tape_input + "BBB")
    head  = 1
    state = "q0"
    while state != "qf":
        sym  = tape[head] if head < len(tape) else "B"
        next_state, write, move = delta[(state, sym)]
        tape[head] = write
        head += 1 if move == "R" else -1
        state = next_state
    return "".join(tape).strip("B")

Tese de Church-Turing e o Problema da Parada

# Tese de Church-Turing e Decidibilidade

# Tese de Church-Turing (1936):
# "Todo processo que intuitivamente consideramos computável
#  pode ser computado por uma Máquina de Turing."
# → Python, C, Rust, JavaScript são Turing-completos.
#   Qualquer um pode simular qualquer outro.

# Problemas DECIDÍVEIS (TM sempre para e aceita ou rejeita):
# - Testar se uma string está em uma linguagem regular
# - Testar se um número é primo
# - Ordenar uma lista

# Problema da PARADA (Halting Problem) — INDECIDÍVEL:
# "Dado um programa P e uma entrada I,
#  P termina em I?" → nenhuma TM decide isso

# Prova (Turing, 1936) — por contradição:
# Suponha que HALT(P, I) existe e decide o problema
def paradoxo(P):
    if HALT(P, P):   # se P para quando roda sobre si mesmo...
        loop()       # ...então loop
    else:            # se P não para...
        return       # ...então para

# paradoxo(paradoxo): contradição em ambos os casos → HALT não existe
# Implicação prática: não existe ferramenta que analise
# arbitrariamente código e decida se ele termina.
Máquina de Turing Universal (UTM): Turing também provou que existe uma MTU — uma Máquina de Turing que recebe como entrada a descrição de qualquer outra Máquina de Turing e sua entrada, e simula a execução. Esse conceito é a base teórica do computador de propósito geral: o hardware é fixo, mas executa qualquer programa codificado como dados. Seu computador é literalmente uma UTM em silício.
④ Projeto para programar

Construindo simuladores de TM

Mini projeto: implemente um simulador genérico de Máquina de Turing em Python. Represente a tabela de transições como um dicionário e a fita como um defaultdict(lambda: "B"). Execute a TM do incremento binário e verifique os resultados para "0", "1", "1111", "10101".

Projeto principal: implemente uma Máquina de Turing de 2 fitas para multiplicar dois números unários (representados como sequências de 1s). Use a segunda fita como espaço de trabalho. Compare com a versão de 1 fita para entender como múltiplas fitas simplificam algoritmos (ambas têm o mesmo poder, mas a 2-fitas é polinomialmente mais eficiente).

Desafio extra: pesquise o Busy Beaver problem — qual TM de N estados escreve o maior número de 1s antes de parar? Para N=2 o máximo é 4; para N=5, 4098. Para N=6 é desconhecido. Implemente um enumerador e runner para explorar TMs de 2-3 estados e encontrar a campeã. O problema é indecidível — mas para N pequeno é enumerável.

⑤ Exercícios rápidos

Teste sua intuição

Qual é a relação entre uma Máquina de Turing e um computador moderno?
O que Turing provou sobre o Problema da Parada (Halting Problem)?
O que significa dizer que uma linguagem é "Turing-completa"?
⑥ Aplicações no mundo real

Onde você encontra isso

🤖

Teoria da complexidade — P vs NP

A questão aberta mais famosa da ciência da computação: P (problemas decidíveis em tempo polinomial por uma TM determinística) é igual a NP (decidíveis em tempo polinomial por uma TM não-determinística)? Se P = NP, a criptografia moderna colapsa. Se P ≠ NP (o que a maioria acredita), então existem problemas fundamentalmente difíceis de resolver mas fáceis de verificar — como fatoração de inteiros grandes.

🔬

Verificação formal — model checking

Ferramentas como TLA+ (Amazon), Alloy e SPIN usam autômatos e lógica temporal para verificar sistemas distribuídos. A AWS verifica formalmente algoritmos de consenso e protocolos de S3 e DynamoDB com TLA+. O model checker explora todos os estados possíveis do sistema — um autômato finito sobre os estados do protocolo — e verifica se propriedades (como "nunca dois líderes ao mesmo tempo") se mantêm.

🧠

LLMs e computabilidade

Um modelo de linguagem com contexto finito é estritamente mais fraco que uma Máquina de Turing — é equivalente a um autômato com pilha limitada. Mas com ferramentas externas (memória, código de execução), um LLM pode orquestrar sistemas Turing-completos. A pergunta de se redes neurais com pesos fixos podem aprender algoritmos gerais é uma questão aberta em teoria da computação — análoga a perguntar se são Turing-completas de forma útil.

← Anterior: Gramáticas e linguagens ✓ Concluir trilha: Lógica e teoria