\documentclass[a4paper]{article}
\usepackage{amsmath, amssymb, amsfonts}
\usepackage[italian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage[a4paper, total={6in, 8in}]{geometry}
\usepackage{minted}
\usepackage{mathpazo}
\usepackage{fancyhdr}
\usepackage{amsthm}
\usepackage{tikz}
\usetikzlibrary{automata,positioning}
\usepackage{enumitem} % Per liste personalizzate

% Definizione degli ambienti
\newtheorem{theorem}{Teorema}
\newtheorem{definition}{Definizione}
\newtheorem{example}{Esempio}
\newtheorem{lemma}{Lemma}
\newtheorem{proposition}{Proposizione}

\hypersetup{
    pdftitle={Lezione di Informatica Teorica: Teorema di Cook},
    pdfauthor={Appunti da Trascrizione AI}
}

\pagestyle{fancy}
\fancyhf{}
\fancyhead[L]{\textit{Lezione di Informatica Teorica}}
\fancyfoot[C]{\thepage}

\title{Lezione di Informatica Teorica: Il Teorema di Cook}
\author{Appunti da Trascrizione Automatica}
\date{\today}

\begin{document}
\maketitle
\tableofcontents
\newpage

\section{Introduzione e Richiami}

La lezione odierna chiude la parte dedicata alla classe di complessità \textbf{NP}, concentrandosi sul Teorema di Cook, fondamentale per la comprensione della teoria della NP-completezza.

\subsection{Richiami sulle Classi di Complessità}

\begin{definition}[Classe NP]
La classe \textbf{NP} (Nondeterministic Polynomial time) include tutti i problemi di decisione (linguaggi) che possono essere decisi da una \emph{Macchina di Turing Non Deterministiche (NDTM)} in tempo polinomiale.
\end{definition}

\begin{definition}[NP-Hard]
Un linguaggio $L$ è \textbf{NP-Hard} se per ogni linguaggio $L'$ appartenente a NP, $L'$ si riduce polinomialmente a $L$ ($L' \le_P L$). Intuitivamente, i problemi NP-Hard sono \textit{almeno difficili quanto} tutti i problemi in NP.
\end{definition}

\begin{definition}[NP-Complete]
Un linguaggio $L$ è \textbf{NP-Complete} se $L$ è sia in NP che NP-Hard.
\end{definition}

\subsection{Il Problema della Dimostrazione Iniziale}
Fino ad ora, per dimostrare che un problema $B$ è NP-Hard, abbiamo usato la \emph{transitività delle riduzioni polinomiali}:
\begin{theorem}[Proprietà di Transitività]
Se $A$ è NP-Hard e $A \le_P B$, allora $B$ è NP-Hard.
\end{theorem}
Questo metodo ci ha permesso di dimostrare che molti problemi (e.g., Knapsack, Exact Cover, 3-SAT, Vertex Cover, Click) sono NP-Complete o NP-Hard, partendo da un problema già noto come NP-Hard.
Tuttavia, questo approccio si basa sull'assunzione che esista almeno un problema NP-Hard conosciuto. Come abbiamo dimostrato che SAT è NP-Hard? In realtà, non l'abbiamo ancora fatto. Tutte le nostre dimostrazioni precedenti si fondano su questa assunzione.

Per dimostrare che \textbf{SAT} è NP-Hard, non possiamo usare la transitività, poiché non abbiamo un problema NP-Hard di partenza. Dobbiamo usare la definizione originale di NP-Hardness: mostrare che \emph{ogni linguaggio $L$ in NP si riduce polinomialmente a SAT}.
Il problema è che ci sono infiniti linguaggi in NP, quindi non possiamo fornire una riduzione specifica per ognuno di essi. È necessario un approccio generico. Questo è il contenuto del \textbf{Teorema di Cook}.

\section{Teorema di Cook: SAT è NP-Completo}

\begin{theorem}[Teorema di Cook]
Il problema SAT (Boolean Satisfiability Problem) è NP-Complete.
\end{theorem}

Per dimostrare che SAT è NP-Complete, dobbiamo mostrare due cose:
\begin{enumerate}
    \item SAT è in NP. (Questo è già noto: una soluzione (assegnamento di verità) può essere verificata in tempo polinomiale).
    \item SAT è NP-Hard. (Questo è l'obiettivo della lezione: mostrare che $L \le_P \text{SAT}$ per ogni $L \in \text{NP}$).
\end{enumerate}

\subsection{La Strategia della Riduzione}
L'idea chiave della dimostrazione di NP-Hardness di SAT è costruire una riduzione polinomiale da un \emph{generico} linguaggio $L \in \text{NP}$ a SAT.
Poiché $L \in \text{NP}$, per definizione, esiste una \emph{Macchina di Turing Non Deterministica (NDTM)} $M$ che decide $L$ in tempo polinomiale. Sia $P(n)$ il polinomio che definisce il tempo di esecuzione di $M$ per un input di lunghezza $n$.

La riduzione $f$ prenderà in input una stringa $W$ (istanza generica di $L$) e produrrà una \emph{formula booleana} $F(W)$ in forma congiuntiva normale (CNF). La formula $F(W)$ sarà soddisfacibile se e solo se la macchina $M$ accetta la stringa $W$.

\[ W \in L \iff M \text{ accetta } W \iff F(W) \text{ è soddisfacibile} \]

La funzione di riduzione $f$ (che costruisce $F(W)$) dovrà essere calcolabile in tempo polinomiale.

\subsubsection{Assunzioni semplificative sulla Macchina di Turing $M$}
Per facilitare la costruzione della formula $F(W)$, facciamo alcune assunzioni semplificative su $M$:
\begin{itemize}
    \item Il nastro di $M$ è \textbf{semi-infinito} (parte dalla cella 0, non va a sinistra di 0). Qualsiasi NDTM può essere convertita in una equivalente con nastro semi-infinito.
    \item $M$ esegue \textbf{esattamente $P(n)$ passi} per ogni ramo di computazione. Se un ramo termina prima (accetta o rifiuta), $M$ continua a ciclare in uno stato finale fittizio per riempire i restanti passi fino a $P(n)$. Questo semplifica la gestione del tempo.
    \item $M$ non scrive mai il simbolo "blank" (si può assumere che utilizzi un simbolo speciale diverso dal blank per marcare celle vuote o usate).
    \item Il numero di stati $R$ di $M$ e il numero di simboli dell'alfabeto del nastro $\Gamma$ sono costanti e indipendenti dalla lunghezza dell'input $n$.
\end{itemize}
Dato che $M$ compie al più $P(n)$ passi, l'unica parte del nastro che può essere letta o scritta è quella compresa tra la cella 0 e la cella $P(n)$ (inclusa).

\subsection{Variabili Proposizionali per la Simulazione}
Per simulare il comportamento di $M$ su $W$ all'interno di una formula booleana, definiamo un insieme di variabili proposizionali. Queste variabili rappresenteranno lo stato di $M$ in ogni istante di tempo e su ogni cella del nastro.
Sia $N_{max} = P(n)$ il numero massimo di passi e celle rilevanti.

\begin{enumerate}[label=\alph*)]
    \item \textbf{$Q_{i,k}$} (Stato): Variabile booleana vera se e solo se la macchina $M$ al passo $i$ si trova nello stato $q_k$.
    \begin{itemize}
        \item $i \in [0, N_{max}]$ (passi di computazione)
        \item $k \in [0, R-1]$ (indici degli stati, dove $R$ è il numero totale di stati di $M$)
        \item Numero di variabili: $O(N_{max} \cdot R) = O(P(n))$, che è polinomiale.
    \end{itemize}
    \item \textbf{$H_{i,j}$} (Head - Testina): Variabile booleana vera se e solo se la testina di $M$ al passo $i$ si trova sulla cella $j$ del nastro.
    \begin{itemize}
        \item $i \in [0, N_{max}]$
        \item $j \in [0, N_{max}]$ (indici delle celle del nastro)
        \item Numero di variabili: $O(N_{max}^2) = O(P(n)^2)$, che è polinomiale.
    \end{itemize}
    \item \textbf{$T_{i,j,l}$} (Tape - Nastro): Variabile booleana vera se e solo se al passo $i$ la cella $j$ del nastro contiene il simbolo $\alpha_l$.
    \begin{itemize}
        \item $i \in [0, N_{max}]$
        \item $j \in [0, N_{max}]$
        \item $l \in [0, |\Gamma|-1]$ (indici dei simboli dell'alfabeto del nastro $\Gamma$)
        \item Numero di variabili: $O(N_{max}^2 \cdot |\Gamma|) = O(P(n)^2)$, che è polinomiale.
    \end{itemize}
\end{enumerate}
Il numero totale di variabili proposizionali è polinomiale rispetto alla lunghezza dell'input $n$.

\subsection{Struttura della Formula $F(W)$}
La formula $F(W)$ sarà una congiunzione di quattro macro-clausole (o gruppi di clausole):
\[ F(W) = C \land S \land N \land F \]
Dove:
\begin{itemize}
    \item $C$ (Consistency): Assicura che l'assegnamento di verità alle variabili descriva una configurazione valida della macchina a ogni passo.
    \item $S$ (Start): Codifica la configurazione iniziale della macchina (stato, posizione testina, contenuto del nastro).
    \item $N$ (Next Step): Codifica come la configurazione della macchina evolve da un passo all'altro, seguendo la funzione di transizione di $M$.
    \item $F$ (Finish): Codifica che la macchina raggiunge uno stato di accettazione all'ultimo passo.
\end{itemize}

Prima di definire le macro-clausole, ripassiamo alcune proprietà utili per convertire le implicazioni in CNF:
\begin{itemize}
    \item \textbf{Implicazione}: $A \implies B$ è equivalente a $\neg A \lor B$.
    \item \textbf{Distributività}: $A \land (B \lor C)$ è equivalente a $(A \land B) \lor (A \land C)$.
    \item \textbf{De Morgan}: $\neg (A \land B)$ è equivalente a $\neg A \lor \neg B$.
    \item \textbf{Implicazione annidata}: $A \implies (B \land C)$ è equivalente a $(A \implies B) \land (A \implies C)$.
    \item \textbf{Implicazione annidata (OR)}: $A \implies (B \lor C)$ è equivalente a $(\neg A \lor B \lor C)$.
\end{itemize}

\begin{example}[Esempio di Riformulazione]
Consideriamo la formula: $A \land B \implies (C \land D) \lor (E \land F)$
\begin{enumerate}
    \item Applichiamo la distributività sulla parte destra dell'OR:
    $A \land B \implies (C \lor E) \land (C \lor F) \land (D \lor E) \land (D \lor F)$
    \item Applichiamo l'implicazione annidata: $X \implies Y \land Z$ è $(X \implies Y) \land (X \implies Z)$.
    Questo si traduce in una congiunzione di 4 implicazioni:
    $(A \land B \implies C \lor E) \land$ \\
    $(A \land B \implies C \lor F) \land$ \\
    $(A \land B \implies D \lor E) \land$ \\
    $(A \land B \implies D \lor F)$
    \item Ogni implicazione $X \implies Y$ può essere riscritta come $\neg X \lor Y$:
    $(\neg (A \land B) \lor C \lor E) \land$ \\
    $(\neg (A \land B) \lor C \lor F) \land$ \\
    $(\neg (A \land B) \lor D \lor E) \land$ \\
    $(\neg (A \land B) \lor D \lor F)$
    \item Infine, applichiamo De Morgan: $\neg (A \land B)$ è $\neg A \lor \neg B$:
    $(\neg A \lor \neg B \lor C \lor E) \land$ \\
    $(\neg A \lor \neg B \lor C \lor F) \land$ \\
    $(\neg A \lor \neg B \lor D \lor E) \land$ \\
    $(\neg A \lor \neg B \lor D \lor F)$
\end{enumerate}
Questa forma finale è una formula in CNF, composta da clausole di 4 letterali.
\end{example}

\subsubsection{1. Consistenza (C)}
La clausola $C$ impone vincoli affinché l'assegnamento di verità rappresenti una configurazione coerente della macchina di Turing ad ogni passo.

\begin{itemize}
    \item \textbf{Unicità dello stato per passo}: Ad ogni passo $i$, la macchina si trova in \emph{al più un} stato.
    \[ \bigwedge_{i=0}^{N_{max}} \bigwedge_{k=0}^{R-1} \bigwedge_{k'=k+1}^{R-1} (\neg Q_{i,k} \lor \neg Q_{i,k'}) \]
    Questo è equivalente a $Q_{i,k} \implies \neg Q_{i,k'}$ (se è nello stato $k$, non può essere nello stato $k'$).
    \item \textbf{Unicità della posizione della testina per passo}: Ad ogni passo $i$, la testina si trova in \emph{al più una} cella.
    \[ \bigwedge_{i=0}^{N_{max}} \bigwedge_{j=0}^{N_{max}} \bigwedge_{j'=j+1}^{N_{max}} (\neg H_{i,j} \lor \neg H_{i,j'}) \]
    Questo è equivalente a $H_{i,j} \implies \neg H_{i,j'}$ (se la testina è in $j$, non può essere in $j'$).
    \item \textbf{Unicità del simbolo per cella per passo}: Ad ogni passo $i$ e per ogni cella $j$, la cella contiene \emph{al più un} simbolo.
    \[ \bigwedge_{i=0}^{N_{max}} \bigwedge_{j=0}^{N_{max}} \bigwedge_{l=0}^{|\Gamma|-1} \bigwedge_{l'=l+1}^{|\Gamma|-1} (\neg T_{i,j,l} \lor \neg T_{i,j,l'}) \]
    Questo è equivalente a $T_{i,j,l} \implies \neg T_{i,j,l'}$ (se la cella $j$ contiene $\alpha_l$, non può contenere $\alpha_{l'}$).
\end{itemize}
Il numero totale di clausole in $C$ è polinomiale. Queste clausole assicurano che l'assegnamento di verità sia internamente consistente.

\subsubsection{2. Inizio (S)}
La clausola $S$ impone la configurazione iniziale della macchina $M$ all'istante $i=0$.
Assumiamo $q_0$ sia lo stato iniziale di $M$. La stringa input è $W = w_0 w_1 \dots w_{n-1}$.
\[ S = Q_{0,q_0} \land H_{0,0} \land \left( \bigwedge_{j=0}^{n-1} T_{0,j,w_j} \right) \land \left( \bigwedge_{j=n}^{N_{max}} T_{0,j,blank} \right) \]
\begin{itemize}
    \item $Q_{0,q_0}$: La macchina è nello stato iniziale $q_0$ al passo 0.
    \item $H_{0,0}$: La testina è sulla cella 0 al passo 0.
    \item $T_{0,j,w_j}$ per $j \in [0, n-1]$: Le prime $n$ celle del nastro contengono l'input $W$.
    \item $T_{0,j,blank}$ per $j \in [n, N_{max}]$: Tutte le celle successive sono blank.
\end{itemize}
Anche $S$ è composta da un numero polinomiale di clausole (tutte unitarie).

\subsubsection{3. Prossimo Passo (N)}
La clausola $N$ codifica il comportamento passo-passo della macchina. È divisa in tre sottocategorie:

\paragraph{3a. Inerzia ($N_I$): Celle distanti dalla testina}
Le celle del nastro che non sono sotto la testina al passo $i$ non cambiano il loro contenuto al passo $i+1$.
\[ N_I = \bigwedge_{i=0}^{N_{max}-1} \bigwedge_{j=0}^{N_{max}} \bigwedge_{l=0}^{|\Gamma|-1} ( (\neg H_{i,j} \land T_{i,j,l}) \implies T_{i+1,j,l} ) \]
Questa implicazione, convertita in CNF, diventa $\neg(\neg H_{i,j} \land T_{i,j,l}) \lor T_{i+1,j,l}$, che è equivalente a $(H_{i,j} \lor \neg T_{i,j,l} \lor T_{i+1,j,l})$.

\paragraph{3b. Transizione di stato/testina/simbolo ($N_H$): Cella sotto la testina}
Le celle sotto la testina cambiano il loro contenuto e la posizione della testina si sposta, in base alla funzione di transizione $\delta$ di $M$. Per ogni $i \in [0, N_{max}-1]$, $j \in [0, N_{max}]$ e per ogni $k \in [0, R-1]$, $l \in [0, |\Gamma|-1]$:
Se al passo $i$, la macchina è nello stato $q_k$, la testina è in posizione $j$, e la cella $j$ contiene $\alpha_l$, allora devono essere vere le condizioni sul prossimo passo ($i+1$) dettate dalla funzione di transizione.

Per ogni tupla $(q_k, \alpha_l)$, sia $\delta(q_k, \alpha_l) = \{ (q_{k_1}, \alpha_{l_1}, D_1), (q_{k_2}, \alpha_{l_2}, D_2), \dots \}$ dove $D_x \in \{L, R, S\}$ (Left, Right, Stay).
\begin{align*}
N_H = \bigwedge_{i=0}^{N_{max}-1} \bigwedge_{j=0}^{N_{max}} \bigwedge_{k=0}^{R-1} \bigwedge_{l=0}^{|\Gamma|-1} & ( (Q_{i,k} \land H_{i,j} \land T_{i,j,l}) \implies \\
& \bigvee_{(q_{k'}, \alpha_{l'}, D) \in \delta(q_k, \alpha_l)} (Q_{i+1,k'} \land T_{i+1,j,l'} \land H_{i+1,j'}) )
\end{align*}
Dove $j'$ è la nuova posizione della testina basata su $D$:
\begin{itemize}
    \item Se $D = R$, allora $j' = j+1$.
    \item Se $D = L$, allora $j' = j-1$.
    \item Se $D = S$, allora $j' = j$.
\end{itemize}
(Occorre gestire i casi limite di $j=0$ per $D=L$ e $j=N_{max}$ per $D=R$, assicurando che la testina non esca dai limiti del nastro rilevante o che tali transizioni non siano ammesse).
Questa formula, pur complessa, è polinomiale in dimensione e può essere trasformata in CNF.

\paragraph{3c. Padding ($N_P$): Ripetizione degli stati finali/bloccanti}
Questa clausola assicura che tutti i rami di computazione abbiano lunghezza $P(n)$, ripetendo la configurazione finale (accettante o bloccante) fino al passo $P(n)$.
\begin{itemize}
    \item \textbf{Se M entra in uno stato finale accettante $q_{acc}$}:
    \[ \bigwedge_{i=0}^{N_{max}-1} \bigwedge_{j=0}^{N_{max}} \bigwedge_{l=0}^{|\Gamma|-1} ( (Q_{i,q_{acc}} \land H_{i,j} \land T_{i,j,l}) \implies (Q_{i+1,q_{acc}} \land H_{i+1,j} \land T_{i+1,j,l}) ) \]
    Questa clausola afferma che se al passo $i$ la macchina è nello stato accettante $q_{acc}$, con testina in $j$ e simbolo $\alpha_l$, allora al passo $i+1$ la macchina rimane nello stesso stato, la testina non si muove e il contenuto del nastro rimane invariato.
    \item \textbf{Se M si blocca (nessuna transizione definita)}:
    Per ogni stato $q_k$ e simbolo $\alpha_l$ tale che $\delta(q_k, \alpha_l) = \emptyset$:
    \[ \bigwedge_{i=0}^{N_{max}-1} \bigwedge_{j=0}^{N_{max}} \bigwedge_{k \text{ s.t. } \delta(q_k, \alpha_l)=\emptyset} \bigwedge_{l=0}^{|\Gamma|-1} ( (Q_{i,k} \land H_{i,j} \land T_{i,j,l}) \implies (Q_{i+1,k} \land H_{i+1,j} \land T_{i+1,j,l}) ) \]
    Questa clausola assicura che se la macchina si trova in una configurazione in cui non sono definite transizioni, essa "stalla", ripetendo la stessa configurazione nel passo successivo.
\end{itemize}
Queste clausole garantiscono che tutte le computazioni, sia quelle accettanti che quelle bloccanti, vengano estese fino alla lunghezza $P(n)$.

\subsubsection{4. Finale (F)}
La clausola $F$ impone che la macchina $M$ debba terminare in uno stato accettante all'ultimo passo $N_{max} = P(n)$.
Assumiamo $q_{acc}$ sia lo stato accettante finale.
\[ F = Q_{N_{max}, q_{acc}} \]
Questa è una singola clausola unitaria, che forza l'assegnamento di verità a $Q_{P(n),q_{acc}}$ ad essere vero.

\section{Conclusione della Dimostrazione}

La formula completa $F(W) = C \land S \land N \land F$ ha le seguenti proprietà:
\begin{enumerate}
    \item \textbf{Dimensione Polinomiale}: Tutte le parti di $F(W)$ sono congiunzioni di un numero polinomiale di clausole, e ogni clausola ha una dimensione costante. Pertanto, la dimensione totale di $F(W)$ è polinomiale rispetto alla lunghezza dell'input $n$. Questo significa che la funzione di riduzione $f$ è calcolabile in tempo polinomiale.
    \item \textbf{Forma CNF}: Come dimostrato nell'esempio e per la natura delle implicazioni utilizzate, $F(W)$ può essere espressa in forma congiuntiva normale.
    \item \textbf{Correttezza}:
    \begin{itemize}
        \item Se $W \in L$: Per definizione di NP, esiste almeno un ramo di computazione accettante di $M$ su $W$ che termina entro $P(n)$ passi. Un assegnamento di verità che descrive fedelmente questo ramo di computazione renderà $F(W)$ vera, quindi $F(W)$ è soddisfacibile.
        \item Se $W \notin L$: Nessun ramo di computazione di $M$ su $W$ accetta. Qualsiasi assegnamento di verità che tenti di rendere $F(W)$ vera dovrà violare una delle clausole (es. una transizione non valida in $N$, o non raggiungendo $q_{acc}$ in $F$), rendendo $F(W)$ insoddisfacibile.
    \end{itemize}
\end{enumerate}

Quindi, abbiamo dimostrato che per ogni linguaggio $L \in \text{NP}$, $L \le_P \text{SAT}$. Per definizione, questo implica che SAT è \textbf{NP-Hard}.
Poiché SAT è anche in NP (come ricordato all'inizio), ne consegue che \textbf{SAT è NP-Complete}.

Questo risultato è di fondamentale importanza, in quanto stabilisce SAT come il "problema prototipo" della classe NP-Complete. Molti altri problemi NP-Complete vengono dimostrati tali attraverso riduzioni da SAT.
Inoltre, esistono programmi chiamati "SAT solvers" che cercano di determinare la soddisfacibilità di formule booleane. La capacità di ridurre problemi NP-Complete a SAT significa che questi SAT solvers possono essere usati per risolvere istanze di un'ampia varietà di problemi difficili, trasformandoli in problemi di soddisfacibilità booleana.

\end{document}