1. A revolução algorítmica silenciosa da ciência da computação
Abra quase qualquer livro de ciência da computação teórica e você encontrará um aviso solene: «O Problema da Satisfatibilidade Booleana (SAT) é NP-completo. Como sua complexidade de tempo no pior caso cresce exponencialmente como \(O(2^n)\) em relação ao número de variáveis \(n\), instâncias grandes são fundamentalmente intratáveis.»
Do ponto de vista puramente combinatório, o argumento parece irrefutável. Uma fórmula proposicional sobre \(n = 100\) variáveis booleanas admite \(2^{100} \approx 1.26 \times 10^{30}\) atribuições de verdade possíveis. Mesmo que um supercomputador pudesse verificar \(10^{12}\) atribuições por segundo desde o Big Bang (há 13,8 bilhões de anos), teria explorado menos de \(10^{-8}\) do espaço de busca até hoje. De fato, por volta de 1990, os melhores solucionadores SAT travavam diante de fórmulas com apenas \(100\) variáveis e \(400\) cláusulas.
No entanto, no século XXI, a engenharia desafia essa intuição milhões de vezes por dia. Solucionadores SAT e SMT industriais modernos — como MiniSat, Glucose, Lingeling, CaDiCaL, Kissat e o Z3 da Microsoft Research — resolvem rotineiramente instâncias de verificação formal de microprocessadores, otimização de compiladores e resolução de dependências de pacotes com \(10.000.000\) (\(10^7\)) de variáveis e \(50.000.000\) (\(5 \times 10^7\)) de cláusulas em questão de segundos.
Quando Donald E. Knuth publicou o Volume 4B (Seção 7.2.2.2) de The Art of Computer Programming em 2015, dedicou mais de 300 páginas exclusivamente aos solucionadores SAT, saudando os avanços do final dos anos 1990 e início dos anos 2000 como uma das maiores revoluções algorítmicas da história da computação. Se antes reduzir um problema a SAT significava provar sua intratabilidade prática, hoje traduzir um problema combinatório difícil para SAT é a estratégia definitiva para resolvê-lo instantaneamente.
2. Forma Normal Conjuntiva (CNF), transformação de Tseitin e o teorema de Cook-Levin (1971)
2.1 Forma Normal Conjuntiva (CNF) e codificação linear de Tseitin
Um solucionador SAT opera sobre uma fórmula proposicional \(\Phi\) definida sobre \(n\) variáveis booleanas \(V = \{x_1, \dots, x_n\}\) com valores em \(\{0, 1\}\). Um literal \(\ell \in \{x_v, \neg x_v\}\) é uma variável ou sua negação; uma cláusula \(C_i = \bigvee_{j=1}^{k_i} \ell_{i,j}\) é uma disjunção (OR lógico, \(\lor\)) de literais; e uma fórmula na Forma Normal Conjuntiva (CNF) é uma conjunção (AND lógico, \(\land\)) de \(m\) cláusulas:
\[\Phi(x_1, \dots, x_n) = \bigwedge_{i=1}^m C_i = \bigwedge_{i=1}^m \left(\bigvee_{j=1}^{k_i} \ell_{i,j}\right)\]Se convertêssemos ingenuamente um circuito lógico arbitrário para CNF aplicando repetidamente a lei distributiva \(A \lor (B \land C) \equiv (A \lor B) \land (A \lor C)\), o tamanho da fórmula explodiria exponencialmente. Em 1968, Grigori Tseitin eliminou essa explosão com a transformação de Tseitin: introduz-se uma nova variável auxiliar \(y_g\) para a saída de cada porta lógica \(g\) do circuito e codifica-se a equivalência local \(y_g \leftrightarrow \text{porta}(a, b)\) com apenas 3 ou 4 cláusulas. Por exemplo, uma porta AND \(y_g \leftrightarrow (a \land b)\) equivale exatamente a três cláusulas:
\[\left(\neg y_g \lor a\right) \;\land\; \left(\neg y_g \lor b\right) \;\land\; \left(y_g \lor \neg a \lor \neg b\right)\]Assim, qualquer circuito com \(|G|\) portas é convertido em tempo e espaço estritamente lineares \(O(|G|)\) em uma fórmula 3-CNF equissatisfatível.
2.2 O teorema de Cook-Levin e o abismo de complexidade entre 2-SAT e 3-SAT
Em 1971, Stephen Cook e Leonid Levin provaram independentemente o teorema de Cook-Levin: todo problema de decisão na classe \(\text{NP}\) pode ser reduzido em tempo polinomial a SAT codificando o quadro de computação espaço-temporal de uma máquina de Turing não determinística durante \(p(n)\) passos (estados \(S_{t,q}\), posição da cabeça \(H_{t,i}\), símbolos de fita \(T_{t,i,\sigma}\)) como uma fórmula CNF.
Quando cada cláusula possui no máximo \(k\) literais (\(k\)-SAT), existe um abismo entre \(k = 2\) e \(k = 3\):
- 2-SAT é resolvido em tempo linear \(O(n + m)\): uma cláusula binária \((\ell_1 \lor \ell_2)\) equivale a duas implicações direcionadas determinísticas \((\neg \ell_1 \Rightarrow \ell_2) \land (\neg \ell_2 \Rightarrow \ell_1)\). No grafo de implicação sobre os \(2n\) literais, o algoritmo de Tarjan calcula as componentes fortemente conexas (SCC) em tempo \(O(n+m)\): a fórmula é satisfatível se e somente se nenhuma variável \(x_v\) pertence à mesma SCC que \(\neg x_v\).
- 3-SAT é NP-completo: uma cláusula ternária \((\ell_1 \lor \ell_2 \lor \ell_3)\) equivale a \((\neg \ell_1 \land \neg \ell_2) \Rightarrow \ell_3\), uma hiperaresta que exige duas premissas simultâneas. Sob a Hipótese do Tempo Exponencial (ETH), nenhum algoritmo determinístico resolve 3-SAT em tempo subexponencial \(2^{o(n)}\) no pior caso.
3. O algoritmo DPLL clássico (1962) e a tragédia do backtracking cronológico
O algoritmo DPLL (Davis, Putnam, Logemann, Loveland, 1962) combina busca em profundidade com a propagação unitária (Unit Propagation / Boolean Constraint Propagation, BCP):
Propagação Unitária (BCP): Se sob a atribuição parcial atual todos os literais de uma cláusula \(C = (\ell_1 \lor \dots \lor \ell_{k-1} \lor \ell_k)\) exceto \(\ell_k\) forem avaliados como
False(\(0\)) e \(\ell_k\) permanecer não atribuído, \(C\) é uma cláusula unitária. O solucionador força imediatamente \(\ell_k = \text{True}\) (\(1\)), disparando uma cascata em dominó de novas propagações unitárias até a saturação ou até falsificar todos os literais de alguma cláusula (Conflito).
Por que o DPLL esbarrava na barreira de 100 variáveis? Por causa do backtracking cronológico. Se o solucionador define \(x_1 = 0\) no nível de decisão \(d = 1\), faz 48 escolhas totalmente independentes \(x_2, \dots, x_{49}\) nos níveis \(d = 2 \dots 49\), e no nível \(d = 50\) escolhe \(x_{50} = 1\), gerando um conflito imediato causado unicamente por \(x_1 = 0\) e \(x_{50}\) através das cláusulas \((x_1 \lor \neg x_{50} \lor \pm x_{51})\) e \((x_1 \lor x_{50} \lor \pm x_{52})\), o DPLL recua apenas um nível para \(d = 49\), inverte a variável inocente \(x_{49}\) e repete exatamente o mesmo fracasso em \(2^{48} \approx 2.81 \times 10^{14}\) subárvores inúteis (thrashing)!
4. A revolução CDCL: grafos de implicação, resolução 1-UIP e salto não cronológico
Introduzido no GRASP (Marques-Silva & Sakallah, 1996) e consagrado no Chaff (Moskewicz et al., 2001), o Aprendizado de Cláusulas Dirigido por Conflitos (CDCL) transformou cada contradição em aprendizado permanente.
flowchart TD
Start["Entrada: Fórmula CNF Φ (Nível de decisão d = 0)"] --> BCP["Propagação de Restrições Booleanas (BCP)<br/>Propagação rápida via Dois Literais Vigiados (2WL)"]
BCP --> CheckConflict{"Cláusula de conflito κ<br/>falsificada?"}
CheckConflict -- "Sem conflito" --> CheckAllAssigned{"Todas as variáveis<br/>atribuídas?"}
CheckAllAssigned -- "Sim" --> SAT["SATISFATÍVEL (SAT)<br/>Retornar atribuição σ"]
CheckAllAssigned -- "Não" --> Branch["Ramificação (Decision): d ← d + 1<br/>Escolher variável com máxima atividade VSIDS<br/>Atribuir polaridade via Phase Saving"]
Branch --> BCP
CheckConflict -- "Sim (Conflito!)" --> CheckLevelZero{"No nível raiz<br/>d == 0?"}
CheckLevelZero -- "Sim" --> UNSAT["INSATISFATÍVEL (UNSAT)<br/>Emitir certificado de prova DRAT"]
CheckLevelZero -- "Não (d > 0)" --> Analyze["Análise de Conflito (Resolução 1-UIP)<br/>Percorrer o grafo de implicação para trás a partir de κ<br/>Resolver até restar apenas 1 literal do nível d (First UIP)"]
Analyze --> Learn["Aprender Cláusula e Incrementar VSIDS<br/>Adicionar C_learned ao banco (calcular LBD)<br/>Aumentar atividade VSIDS das variáveis resolvidas"]
Learn --> Backjump["Salto Não Cronológico (Backjump)<br/>Saltar diretamente ao Nível de Asserção d_back (2.º maior nível em C_learned)<br/>C_learned dispara imediatamente como cláusula unitária no BCP!"]
Backjump --> BCP
4.1 Grafo de implicação, resolução booleana e o Primeiro Ponto de Implicação Único (1-UIP)
Cada literal atribuído \(\ell@d\) registra seu nível de decisão \(d\) e sua cláusula antecedente (razão) \(\text{reason}(\ell)\). Quando \(\kappa\) é falsificada, o CDCL inicializa \(C \leftarrow \kappa\) e percorre para trás a pilha de atribuições (Trail) no nível atual \(d_{\text{curr}}\), aplicando iterativamente a regra de resolução booleana:
\[\frac{(A \lor x) \qquad (\neg x \lor B)}{(A \lor B)} = \text{Res}(C_1, C_2, x)\]No grafo de implicação, um vértice \(u@d_{\text{curr}}\) é um Ponto de Implicação Único (UIP) se todo caminho direcionado da decisão \(\ell_{\text{dec}}@d_{\text{curr}}\) até o conflito \(\Lambda\) passa obrigatoriamente por \(u\) (dominador). O UIP mais próximo do conflito é o Primeiro UIP (1-UIP), alcançado no exato instante em que a cláusula de trabalho \(C\) contém apenas um único literal pertencente ao nível de decisão atual \(d_{\text{curr}}\)!
Com \(x_1 = 0@1, x_2 = 0@2, x_3 = 1@3\), a decisão \(x_4 = 1@5\) e as cláusulas \(\omega_1 = (x_1 \lor \neg x_4 \lor x_5)\), \(\omega_2 = (\neg x_4 \lor \neg x_5 \lor x_6)\), \(\omega_3 = (x_2 \lor \neg x_6 \lor x_7)\), \(\omega_4 = (\neg x_3 \lor \neg x_6 \lor x_8)\) gerando o conflito \(\kappa = (\neg x_7 \lor \neg x_8)\), duas etapas de resolução de \(\kappa\) com \(\omega_4\) (pivô \(x_8\)) e \(\omega_3\) (pivô \(x_7\)) produzem a cláusula aprendida 1-UIP:
\[C_{\text{learned}} = \left(x_2@2 \lor \neg x_3@3 \lor \neg x_6@5\right)\]4.2 Salto não cronológico (Backjumping) e nível de asserção
Como os níveis em \(C_{\text{learned}}\) são \(\{5, 3, 2\}\), o solucionador salta diretamente para o segundo nível de decisão mais alto (nível de asserção) \(d_{\text{back}} = 3\)! No nível \(3\), \(x_2@2\) e \(\neg x_3@3\) continuam falsos enquanto \(\neg x_6\) volta a ficar livre, fazendo com que \(C_{\text{learned}}\) atue imediatamente como cláusula unitária assertiva e force \(\neg x_6 = 1@3\) (\(x_6 = 0@3\)) via BCP!
5. Milagres de engenharia: Dois Literais Vigiados (2WL), VSIDS e LBD
- Dois Literais Vigiados (Two-Watched Literals, 2WL, Chaff 2001): Enquanto uma cláusula tiver pelo menos dois literais não falsificados, ela nunca poderá se tornar unitária nem conflitante. O solucionador vigia apenas dois literais \(w_1, w_2\) por cláusula. Ao realizar um backjump, desatribuir variáveis preserva automaticamente o invariante 2WL: o backtracking exige exatamente zero atualizações de ponteiros (\(O(1)\))!
- VSIDS (Variable State Independent Decaying Sum) e Phase Saving: Variáveis envolvidas na resolução de conflitos recebem um bônus \(+\Delta\), enquanto \(\Delta \leftarrow \Delta / \gamma\) (\(\gamma = 0.95\)) cresce após cada conflito, focando a busca no gargalo ativo.
- LBD (Literal Block Distance / Cláusulas «Glue») e reinícios rápidos: O Glucose (Audemard & Simon, 2009) define \(\text{LBD}(C)\) como o número de níveis de decisão distintos em \(C\). Cláusulas com \(\text{LBD} = 2\) (Glue Clauses) conectam diretamente o UIP atual a um único bloco passado e são mantidas permanentemente.
| Dimensão arquitetural | DPLL clássico (1962) | CDCL de 1.ª geração (GRASP / Chaff 2001) | CDCL de ponta (Glucose / CaDiCaL / Kissat) |
|---|---|---|---|
| Estrutura BCP | Contadores completos por cláusula | Dois Literais Vigiados (2WL) (\(O(1)\) no backtrack sem tocar ponteiros) | 2WL com literais de bloqueio inline + listas binárias dedicadas |
| Recuperação | Backtracking cronológico (\(d-1\), thrashing \(2^k\)) | Salto não cronológico ao nível de asserção \(d_{\text{back}}\) | Híbrido de Backjumping e backtracking cronológico local (2018) |
| Aprendizado | Nenhum (árvore sem memória) | Resolução 1-UIP + minimização recursiva | 1-UIP + autossunção on-the-fly, vivificação e eliminação BVE |
| Heurística de ramo | Ordem estática / DLIS | VSIDS (atividade com decaimento exponencial) | Alternância EVSIDS + VMTF + Phase Saving e busca local WalkSAT |
| Gestão de cláusulas | N/A | Exclusão por tamanho e atividade | Gestão em 3 níveis por LBD (cláusulas Glue \(\text{LBD} \le 2\) permanentes) |
6. Implementação completa em Python de um motor CDCL
Abaixo está uma implementação completa em Python de um solucionador CDCL com BCP, grafo de implicação, resolução 1-UIP, salto não cronológico e VSIDS:
| |
7. Física estatística e a transição de fase do \(k\)-SAT aleatório (\(\alpha_c \approx 4.267\))
Em uma fórmula 3-SAT aleatória com \(n\) variáveis e \(m = \alpha n\) cláusulas (densidade \(\alpha = m / n\)), o método do primeiro momento fornece o valor esperado do número de soluções \(\mathbb{E}[Z]\):
\[\mathbb{E}[Z] = 2^n \left(1 - 2^{-3}\right)^m = \left(2 \cdot \left(\frac{7}{8}\right)^\alpha\right)^n \implies \alpha_{\text{annealed}} = \frac{\ln 2}{\ln(8/7)} \approx 5.191\]Por meio do método da cavidade e da quebra de simetria de réplicas de um passo (1RSB) da física de vidros de spin, Mézard, Parisi e Zecchina (2002) determinaram o limiar crítico exato do 3-SAT aleatório:
\[\alpha_c \approx 4.267\]
flowchart TD
subgraph PhaseTransition["Transição de fase do 3-SAT aleatório conforme a densidade α = m/n"]
S1["1. Fase líquida sub-restrita (α < 3.86 : Easy-SAT)<br/>• As soluções formam um único aglomerado gigante conectado em {0,1}^n<br/>• Resolução rápida em tempo polinomial"]
S2["2. Fase vítrea de fragmentação e congelamento (3.86 < α < 4.267 : Hard-SAT)<br/>• Em α_d ≈ 3.86 (transição 1RSB), o espaço de soluções se despedaçada em exponencialmente muitas ilhas isoladas!<br/>• Em α_f ≈ 4.254 as variáveis dentro de cada ilha ficam rigidamente congeladas (Freezing)"]
S3["3. Limiar crítico de satisfatibilidade (α_c ≈ 4.267 : Pico máximo de dificuldade)<br/>• A probabilidade de SAT cai abruptamente de 1 para 0<br/>• Toda refutação por resolução exige comprimento exponencial 2^Ω(n) (Chvátal–Szemerédi)"]
S4["4. Fase sobre-restrita (α > 4.267 : Easy-UNSAT)<br/>• A alta densidade de cláusulas gera contradições em baixa profundidade<br/>• O tamanho da prova diminui conforme α cresce"]
end
S1 -->|"α_d ≈ 3.86 (Fragmentação)"| S2
S2 -->|"α_c ≈ 4.267 (Transição UNSAT)"| S3
S3 -->|"α > 4.267 (Conflitos rasos)"| S4
Em \(\alpha_c \approx 4.267\), o hipergrafo aleatório é um grafo expansor sem gargalos locais, exigindo refutações por resolução de comprimento exponencial \(2^{\Omega(n)}\) (Chvátal & Szemerédi, 1988). Já os problemas industriais reais possuem estrutura modular e conjuntos «Backdoor» pequenos (Ansótegui et al. 2012; Williams, Gomes, Selman 2003), permitindo que o CDCL decomponha 10 milhões de variáveis em segundos.
8. Triunfos no mundo real e a prova matemática de 200 Terabytes
- Matemática assistida por computador e a prova DRAT de 200 TB (Heule, Kullmann, Marek 2016): Usando Cube-and-Conquer, provaram que o Problema Booleano dos Ternos Pitagóricos (\(a^2 + b^2 = c^2\)) é satisfatível até \(N = 7824\) (SAT), mas insatisfatível em \(N = 7825\) (UNSAT), gerando um certificado formal DRAT de 200 Terabytes, além de resolverem o número de Schur \(S(5) = 160\) (2018) e a conjectura de Keller na dimensão 7 (2020).
- Verificação formal de chips EDA (CEC e BMC): circuitos Miter (XOR entre especificação RTL e portas lógicas) garantem equivalência exata antes da fabricação de qualquer CPU/GPU moderna.
- Gerenciadores de pacotes (Debian apt, Rust Cargo, Dart PubGrub, uv) e Solucionadores SMT (\(\text{DPLL}(T)\): Z3 e cvc5): acoplando CDCL a solucionadores de teorias de primeira ordem, impulsionam a execução simbólica e a verificação de segurança em nuvem (AWS Zelkova).
