Ecco gli appunti completi della lezione in formato LaTeX, pronti per la compilazione:

\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}

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

\hypersetup{
    pdftitle={Lezione di Informatica Teorica - Il Problema dell'Arresto e Introduzione agli Automi},
    pdfauthor={Appunti da Trascrizione AI}
}

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

\title{Lezione di Informatica Teorica: Il Problema dell'Arresto e Introduzione agli Automi}
\author{Appunti da Trascrizione Automatica}
\date{\today}

\begin{document}
\maketitle
\tableofcontents
\newpage

\section{Introduzione e Riepilogo}

Questa lezione mira a consolidare i concetti di base della Teoria della Computazione e introdurre gli strumenti formali per la loro analisi. In particolare, approfondiremo il concetto di problema, dimostreremo l'indecidibilità del Problema dell'Arresto e introdurremo gli Automi a Stati Finiti Determinici (DFA) come modello per lo studio dei linguaggi.

\subsection{Richiamo: Definizione di Problema}

Per noi, un problema è una relazione tra un input e un output. Più precisamente:

\begin{definition}[Problema]
Un problema è un sottoinsieme di tutte le possibili coppie formate da una stringa di input e una stringa di output.
Formalmente, un problema è una relazione $R \subseteq \Sigma^* \times \Gamma^*$, dove $\Sigma$ è l'alfabeto degli input e $\Gamma$ è l'alfabeto degli output.
\end{definition}

Quando si analizza un problema, è fondamentale chiarire tre elementi:
\begin{enumerate}
    \item \textbf{Input}: Cosa è l'input? Come viene rappresentato? Qual è il suo significato?
    \item \textbf{Output}: Cosa è l'output? Come viene rappresentato? Qual è il suo significato?
    \item \textbf{Relazione}: Qual è la relazione che lega l'output all'input? Data una specifica istanza di input, quale output ci si aspetta?
\end{enumerate}
Questo processo di chiarificazione è cruciale per evitare ambiguità e per una corretta formulazione del problema.

\section{Il Problema dell'Arresto (Halting Problem)}

\subsection{Definizione Informale}
Il Problema dell'Arresto chiede: "Dato un programma e un input per quel programma, il programma si arresterà (terminerà) o continuerà a girare indefinitamente su quell'input?"

\subsection{Definizione Formale}
Applichiamo la struttura di analisi del problema:

\begin{definition}[Problema dell'Arresto (HALT)]
\textbf{Input}: Una coppia $\langle P, I \rangle$, dove:
\begin{itemize}
    \item $P$ è una stringa che codifica un programma (e.g., codice Python).
    \item $I$ è una stringa che rappresenta l'input per il programma $P$.
\end{itemize}
\textbf{Output}: Una risposta booleana: "sì" o "no".
\textbf{Relazione}: L'output è "sì" se il programma $P$, eseguito con l'input $I$, si arresta. L'output è "no" se il programma $P$, eseguito con l'input $I$, non si arresta (cioè, entra in un loop infinito o non termina in tempo finito).
\end{definition}

Questo problema ha un'enorme utilità pratica per i programmatori, in quanto un compilatore o un ambiente di sviluppo che potesse prevedere la terminazione di un programma sarebbe uno strumento diagnostico potentissimo.

\subsection{Dimostrazione dell'Indecidibilità}
Dimostreremo che il Problema dell'Arresto è \textbf{indecidibile}, ovvero non esiste un algoritmo universale che possa risolvere correttamente il problema per \emph{ogni} possibile coppia $\langle P, I \rangle$. La dimostrazione è per assurdo.

\begin{theorem}[Indecidibilità del Problema dell'Arresto]
Non esiste un algoritmo che, per ogni data coppia $\langle P, I \rangle$ (programma $P$ e input $I$), sia in grado di determinare correttamente se $P$ si arresta su $I$.
\end{theorem}

\begin{proof}[Dimostrazione per Assurdo]
Assumiamo per assurdo che esista una procedura (un algoritmo) perfetta per risolvere il Problema dell'Arresto. Chiamiamo questa procedura \texttt{halt\_checker}.
Questa procedura \texttt{halt\_checker} prende in input due stringhe: una che rappresenta un programma $P$ e un'altra che rappresenta un input $I$ per $P$. Restituisce un valore booleano (\texttt{True} se $P$ si arresta su $I$, \texttt{False} altrimenti).

\begin{minted}[frame=lines,framesep=2mm,linenos]{python}
# Assunzione: questa funzione "magica" esiste
def halt_checker(P_code: str, I_input: str) -> bool:
    """
    Restituisce True se il programma P_code si arresta con I_input,
    False altrimenti.
    """
    pass # La sua implementazione è sconosciuta, ma si assume che funzioni perfettamente
\end{minted}

Ora, useremo questa \texttt{halt\_checker} per costruire un nuovo programma, che chiameremo \texttt{reverse}. Il programma \texttt{reverse} prende in input una singola stringa, che rappresenta il codice di un programma $P$.

\begin{minted}[frame=lines,framesep=2mm,linenos]{python}
def reverse(P_code: str):
    """
    Questo programma prende in input il codice di un altro programma P.
    """
    # Chiama halt_checker passando P_code sia come programma che come input per sé stesso
    halts = halt_checker(P_code, P_code)

    if halts:
        # Se halt_checker ha detto che P_code si arresta su P_code,
        # allora reverse entra in un loop infinito.
        while True:
            pass # Loop infinito
    else:
        # Se halt_checker ha detto che P_code non si arresta su P_code,
        # allora reverse si arresta immediatamente.
        pass # Termina
\end{minted}

Questo programma \texttt{reverse} è un programma Python legittimo. Può essere compilato ed eseguito.
Sia \texttt{code\_reverse} la stringa che rappresenta il codice del programma \texttt{reverse} stesso.

Consideriamo ora l'esecuzione del programma \texttt{reverse} avendo come input il proprio codice, cioè \texttt{reverse(code\_reverse)}.
Ci sono due possibilità per l'esecuzione di \texttt{reverse(code\_reverse)}:

\begin{enumerate}
    \item \textbf{Caso 1: \texttt{reverse(code\_reverse)} si arresta.}
    \begin{itemize}
        \item Se \texttt{reverse(code\_reverse)} si arresta, significa che l'esecuzione è giunta alla linea \texttt{pass} (termina).
        \item Questo accade se e solo se la condizione \texttt{if halts:} era falsa, cioè \texttt{halts} era \texttt{False}.
        \item Il valore di \texttt{halts} è determinato dalla chiamata \texttt{halt\_checker(code\_reverse, code\_reverse)}.
        \item Quindi, \texttt{halt\_checker(code\_reverse, code\_reverse)} deve aver restituito \texttt{False}.
        \item Ma per definizione di \texttt{halt\_checker}, se restituisce \texttt{False}, significa che il programma \texttt{code\_reverse} (cioè \texttt{reverse}) \textbf{non si arresta} quando esegue con input \texttt{code\_reverse}.
        \item Abbiamo una contraddizione: abbiamo assunto che \texttt{reverse(code\_reverse)} si arresta, ma la logica interna del programma e l'assunzione di \texttt{halt\_checker} implicano che \textbf{non si arresta}.
    \end{itemize}

    \item \textbf{Caso 2: \texttt{reverse(code\_reverse)} non si arresta (entra in loop).}
    \begin{itemize}
        \item Se \texttt{reverse(code\_reverse)} non si arresta, significa che l'esecuzione è giunta al \texttt{while True: pass} (loop infinito).
        \item Questo accade se e solo se la condizione \texttt{if halts:} era vera, cioè \texttt{halts} era \texttt{True}.
        \item Il valore di \texttt{halts} è determinato dalla chiamata \texttt{halt\_checker(code\_reverse, code\_reverse)}.
        \item Quindi, \texttt{halt\_checker(code\_reverse, code\_reverse)} deve aver restituito \texttt{True}.
        \item Ma per definizione di \texttt{halt\_checker}, se restituisce \texttt{True}, significa che il programma \texttt{code\_reverse} (cioè \texttt{reverse}) \textbf{si arresta} quando esegue con input \texttt{code\_reverse}.
        \item Abbiamo una contraddizione: abbiamo assunto che \texttt{reverse(code\_reverse)} non si arresta, ma la logica interna del programma e l'assunzione di \texttt{halt\_checker} implicano che \textbf{si arresta}.
    \end{itemize}
\end{enumerate}

In entrambi i casi possibili (si arresta o non si arresta), giungiamo a una contraddizione logica. Poiché la costruzione del programma \texttt{reverse} e la sua esecuzione sono perfettamente lecite secondo le regole della computazione, l'unica conclusione possibile è che l'assunzione iniziale fosse falsa.

Pertanto, la procedura \texttt{halt\_checker} \textbf{non può esistere}.
\end{proof}

\subsubsection{Implicazioni della Dimostrazione}
\begin{itemize}
    \item \textbf{Indipendenza dalla Tecnologia}: La dimostrazione non fa alcuna assunzione specifica sul linguaggio di programmazione utilizzato (Python è solo un esempio) o sull'architettura hardware su cui il programma viene eseguito. L'unico requisito è che il programma $P$ possa essere codificato come una stringa e che possa prendere un input, e che la procedura \texttt{halt\_checker} possa essere invocata. Questo rende il risultato universale: il Problema dell'Arresto è indecidibile in qualsiasi modello di computazione "abbastanza potente" da poter implementare un \texttt{halt\_checker} e un \texttt{reverse} (come la Macchina di Turing, che vedremo in seguito).
    \item \textbf{Non Esistenza di Soluzioni Perfette}: Ciò non significa che non si possano scrivere programmi che, in alcuni casi specifici o per determinate classi di input, riescano a determinare la terminazione. Significa che non esiste un algoritmo \emph{generale} e \emph{perfetto} che per \emph{tutti} gli input dia sempre la risposta corretta in un tempo finito. Possiamo avere soluzioni approssimate o euristiche, ma non una soluzione infallibile per ogni caso.
\end{itemize}

\section{Classificazione dei Problemi: Decisione vs. Ricerca}

Abbiamo visto che i problemi possono essere di diversa natura. È utile categorizzarli per facilitarne lo studio.

\begin{example}[Problemi di Grafo]
\begin{enumerate}
    \item \textbf{Problema del Percorso (PATH)}:
    \begin{itemize}
        \item \textbf{Input}: Un grafo $G$, un nodo sorgente $S$, un nodo destinazione $T$.
        \item \textbf{Output}: Un percorso da $S$ a $T$ in $G$.
    \end{itemize}
    \item \textbf{Problema del Ciclo Hamiltoniano (HAMILTONIAN CYCLE)}:
    \begin{itemize}
        \item \textbf{Input}: Un grafo $G$.
        \item \textbf{Output}: "Sì" se esiste un ciclo Hamiltoniano (un ciclo che visita ogni nodo esattamente una volta) in $G$, "No" altrimenti.
    \end{itemize}
\end{enumerate}
\end{example}

Notiamo una differenza fondamentale nell'output:

\begin{definition}[Problema di Ricerca]
Un problema di ricerca è un problema il cui output può essere una stringa arbitraria, come un percorso, un numero, una derivata, una matrice risultante, ecc. L'obiettivo è trovare o calcolare qualcosa.
\end{definition}

\begin{definition}[Problema di Decisione]
Un problema di decisione è un problema il cui output è sempre una risposta booleana: "sì" o "no". L'obiettivo è decidere se una certa proprietà è vera o falsa per l'input dato.
\end{definition}

\subsubsection{Relazione tra Problemi di Ricerca e Problemi di Decisione}
Queste due classi non sono disgiunte. Per ogni problema di ricerca, possiamo definire un problema di decisione correlato.

\begin{example}[PATH come Problema di Decisione]
Consideriamo il problema del percorso come problema di decisione:
\begin{itemize}
    \item \textbf{Input}: Un grafo $G$, un nodo sorgente $S$, un nodo destinazione $T$.
    \item \textbf{Output}: "Sì" se esiste un percorso da $S$ a $T$ in $G$, "No" altrimenti.
\end{itemize}
\end{example}

È evidente che se siamo in grado di risolvere il problema di ricerca (trovare un percorso), siamo automaticamente in grado di risolvere il problema di decisione (sapere se un percorso esiste). Spesso, risolvere il problema di decisione è concettualmente più semplice, poiché richiede solo una risposta binaria. Per questa ragione, nella teoria della computazione, ci si concentra principalmente sui problemi di decisione.

\section{Problemi di Decisione e Linguaggi Formali}

Sebbene i problemi di decisione siano più semplici dei problemi di ricerca, possono comunque avere input di forme molto diverse (grafi, matrici, numeri, ecc.). Per standardizzare lo studio e semplificare l'analisi degli algoritmi, si ricorre al concetto di \textbf{linguaggio formale}.

\subsection{Definizioni Fondamentali}

\begin{definition}[Alfabeto ($\Sigma$)]
Un alfabeto $\Sigma$ è un insieme finito e non vuoto di simboli.
\end{definition}
\begin{example}
$\Sigma = \{a, b, c\}$ \\
$\Sigma = \{0, 1\}$ \\
$\Sigma = \{\text{quadratino}, \text{cerchietto}\}$
\end{example}

\begin{definition}[Parola o Stringa]
Una parola (o stringa) su un alfabeto $\Sigma$ è una sequenza finita di zero o più simboli tratti da $\Sigma$.
\end{definition}
\begin{example}
Se $\Sigma = \{a, b\}$, allora $ababa$, $a$, $b$, $\epsilon$ (stringa vuota) sono parole su $\Sigma$.
\end{example}

\begin{definition}[$\Sigma^*$]
$\Sigma^*$ denota l'insieme di tutte le possibili parole (di qualsiasi lunghezza, inclusa la stringa vuota $\epsilon$) che possono essere formate usando i simboli dell'alfabeto $\Sigma$.
\end{definition}

\begin{definition}[Linguaggio ($L$)]
Un linguaggio $L$ su un alfabeto $\Sigma$ è un qualsiasi sottoinsieme di $\Sigma^*$.
Formalmente, $L \subseteq \Sigma^*$.
\end{definition}
\begin{example}
Se $\Sigma = \{0, 1\}$, il linguaggio di tutte le stringhe binarie che iniziano con '0' potrebbe essere $L = \{0, 00, 01, 000, 001, \ldots\}$.
\end{example}

\subsection{Decidere un Linguaggio}

\begin{definition}[Problema di Decisione di un Linguaggio]
Decidere un linguaggio $L$ significa risolvere il seguente problema:
\textbf{Input}: Una stringa $w \in \Sigma^*$.
\textbf{Output}: "Sì" o "No".
\textbf{Relazione}: L'output è "Sì" se $w \in L$. L'output è "No" se $w \notin L$.
\end{definition}
È importante notare che il linguaggio $L$ fa parte della definizione del problema, non dell'input. L'input è solo la stringa $w$.

\subsection{Conversione di Problemi di Decisione in Problemi di Linguaggio}
Ogni problema di decisione può essere ricondotto (o codificato) come un problema di decisione di un linguaggio. Questo si fa codificando le istanze del problema come stringhe.

\begin{example}[Codifica del Problema PATH come Linguaggio]
Riprendiamo il problema di decisione PATH: "Esiste un percorso da $S$ a $T$ in $G$?".
Possiamo definire un linguaggio $L_{PATH}$ su un opportuno alfabeto $\Sigma$ (ad esempio, simboli per nodi, archi, parentesi, virgole, ecc.) tale che:
\[L_{PATH} = \{ \langle G, S, T \rangle \mid G \text{ è un grafo, } S \text{ e } T \text{ sono nodi in } G, \text{ ed esiste un percorso da } S \text{ a } T \text{ in } G \}.\]
Dove $\langle G, S, T \rangle$ è una stringa che codifica la tripla $(G, S, T)$.
Decidere se una stringa $w$ appartiene a $L_{PATH}$ è equivalente a risolvere il problema di decisione PATH originale.
\end{example}
Questa riduzione ci permette di studiare tutti i problemi di decisione concentrandoci unicamente sulla classe dei problemi di riconoscere linguaggi. Questo è il passo che ci permette di definire modelli di calcolo astratti (gli automi) che non dipendono da specifici linguaggi di programmazione o architetture hardware.

\section{Introduzione agli Automi}

\subsection{Concetto Intuitivo di Automa}
Un automa è un dispositivo astratto (o una macchina) che ha un numero finito di "stati" o "modalità di funzionamento". L'automa riceve segnali o input e, in base al suo stato corrente e al segnale ricevuto, cambia stato e/o produce un output.

\subsection{Esempio: Automa di un Lettore CD}
Consideriamo il comportamento di un lettore CD come un automa per capire il concetto.

\begin{figure}[h!]
    \centering
    \begin{tikzpicture}[
        node distance=2.5cm,
        every state/.style={minimum size=1cm},
        initial text={},
        shorten >=1pt,
        auto
    ]
        % Stati
        \node[state, initial] (standby) {StandBy};
        \node[state, right of=standby, xshift=2cm] (testcd) {Test CD};
        \node[state, above right of=testcd, yshift=0.5cm] (empty_stopped) {Empty Stopped};
        \node[state, below right of=testcd, yshift=-0.5cm] (cd_in_stopped) {CD in Stopped};
        \node[state, above right of=empty_stopped, xshift=2cm] (opened) {Opened};
        \node[state, below right of=cd_in_stopped, yshift=-0.5cm] (playing) {Playing};
        \node[state, right of=playing, xshift=2cm] (paused) {Paused};

        % Transizioni
        \path (standby) edge[bend left=10] node {PWR} (testcd);
        \path (testcd) edge node {No} (empty_stopped);
        \path (testcd) edge node {Yes} (cd_in_stopped);

        % Power off from various states
        \path (empty_stopped) edge[bend left=10] node {PWR} (standby);
        \path (cd_in_stopped) edge[bend left=10] node {PWR} (standby);
        \path (testcd) edge[bend left=20] node {PWR} (standby); % Assuming PWR can turn it off from test state too

        % Eject
        \path (empty_stopped) edge node {EJECT} (opened);
        \path (cd_in_stopped) edge node {EJECT} (opened);
        \path (opened) edge node {EJECT} (testcd); % Closing tray leads to re-test

        % Play, Pause, Stop
        \path (cd_in_stopped) edge[bend left=10] node {PLAY} (playing);
        \path (playing) edge node {PAUSE} (paused);
        \path (paused) edge node {PAUSE} (playing);
        \path (playing) edge[bend right=10] node {STOP} (cd_in_stopped);
        \path (paused) edge[bend left=10] node {STOP} (cd_in_stopped);
    \end{tikzpicture}
    \caption{Automa di un Lettore CD}
\end{figure}

\subsubsection{Spiegazione dell'Automa del Lettore CD}
\begin{itemize}
    \item \textbf{Stati (nodi)}: Rappresentano le diverse modalità operative del lettore (e.g., \texttt{StandBy}, \texttt{Playing}, \texttt{Paused}). Il pallino pieno indica lo stato iniziale (\texttt{StandBy}).
    \item \textbf{Transizioni (frecce)}: Rappresentano il passaggio da uno stato all'altro in risposta a un input (e.g., \texttt{PWR}, \texttt{EJECT}, \texttt{PLAY}). Le etichette sulle frecce indicano l'input che provoca la transizione.
    \item \textbf{Input/Segnali}: Azioni dell'utente (pressione di tasti) o sensori (rilevamento CD).
\end{itemize}
Questo esempio illustra come un automa modellizzi un sistema: lo stato in cui si trova il sistema influenzerà come reagirà al prossimo input.

\subsection{Automi a Stati Finiti Determinici (DFA)}
Gli automi vengono usati in informatica teorica per riconoscere linguaggi formali. Un Automaton a Stati Finiti Determinico (DFA) è il modello più semplice.

\begin{definition}[Automa a Stati Finiti Determinico (DFA)]
Un DFA $M$ è una quintupla $(Q, \Sigma, \delta, q_0, F)$, dove:
\begin{itemize}
    \item $Q$ è un insieme finito di stati.
    \item $\Sigma$ è l'alfabeto di input.
    \item $\delta: Q \times \Sigma \to Q$ è la funzione di transizione, che per ogni stato e ogni simbolo di input determina in modo unico il prossimo stato.
    \item $q_0 \in Q$ è lo stato iniziale.
    \item $F \subseteq Q$ è l'insieme degli stati finali (o accettanti).
\end{itemize}
\end{definition}

Un DFA \textbf{accetta} una stringa se, partendo dallo stato iniziale e processando la stringa simbolo per simbolo, l'automa termina in uno stato accettante. Altrimenti, la stringa viene \textbf{rifiutata}.

\begin{example}[Linguaggio dei Numeri Binari Dispari]
Consideriamo il linguaggio $L_{dispari}$ delle stringhe binarie che rappresentano numeri dispari.
\begin{itemize}
    \item $\Sigma = \{0, 1\}$
    \item Una stringa binaria rappresenta un numero dispari se e solo se il suo ultimo simbolo è '1'. (Es: $1_2=1$, $11_2=3$, $101_2=5$; $0_2=0$, $10_2=2$, $100_2=4$). La stringa vuota $\epsilon$ non rappresenta un numero e non deve essere accettata.
\end{itemize}

Costruiamo un DFA per $L_{dispari}$:
\begin{itemize}
    \item $Q = \{Q_0, Q_1\}$
        \begin{itemize}
            \item $Q_0$: Rappresenta lo stato in cui la stringa letta finora indica un numero pari (o la stringa vuota, o un numero che termina con 0).
            \item $Q_1$: Rappresenta lo stato in cui la stringa letta finora indica un numero dispari (termina con 1).
        \end{itemize}
    \item $q_0 = Q_0$ (Inizialmente, il numero è considerato pari, o non abbiamo ancora letto nulla).
    \item $F = \{Q_1\}$ (Se la stringa termina mentre siamo in questo stato, è un numero dispari).
    \item Funzione di transizione $\delta$:
        \begin{itemize}
            \item $\delta(Q_0, 0) = Q_0$ (Se eravamo in stato 'pari' e leggiamo '0', rimaniamo 'pari').
            \item $\delta(Q_0, 1) = Q_1$ (Se eravamo in stato 'pari' e leggiamo '1', diventiamo 'dispari').
            \item $\delta(Q_1, 0) = Q_0$ (Se eravamo in stato 'dispari' e leggiamo '0', diventiamo 'pari').
            \item $\delta(Q_1, 1) = Q_1$ (Se eravamo in stato 'dispari' e leggiamo '1', rimaniamo 'dispari').
        \end{itemize}
\end{itemize}

\begin{figure}[h!]
    \centering
    \begin{tikzpicture}[
        node distance=2.5cm,
        every state/.style={minimum size=1cm},
        initial text={},
        accepting,
        auto
    ]
        % Stati
        \node[state, initial] (q0) {$Q_0$};
        \node[state, accepting, right of=q0] (q1) {$Q_1$};

        % Transizioni
        \path (q0) edge[loop above] node {0} (q0);
        \path (q0) edge node {1} (q1);
        \path (q1) edge node {0} (q0);
        \path (q1) edge[loop above] node {1} (q1);
    \end{tikzpicture}
    \caption{DFA per il Linguaggio dei Numeri Binari Dispari}
\end{figure}

\subsubsection{Esempio di Tracciamento}
Stringa di input: \texttt{101}
\begin{enumerate}
    \item Inizia in $Q_0$.
    \item Legge '1': $\delta(Q_0, 1) = Q_1$. Automa si sposta in $Q_1$.
    \item Legge '0': $\delta(Q_1, 0) = Q_0$. Automa si sposta in $Q_0$.
    \item Legge '1': $\delta(Q_0, 1) = Q_1$. Automa si sposta in $Q_1$.
    \item Stringa terminata. L'automa si trova in $Q_1$, che è uno stato accettante.
\end{enumerate}
Quindi, la stringa \texttt{101} (che rappresenta il numero 5, dispari) è accettata.

Stringa di input: \texttt{110}
\begin{enumerate}
    \item Inizia in $Q_0$.
    \item Legge '1': $\delta(Q_0, 1) = Q_1$. Automa si sposta in $Q_1$.
    \item Legge '1': $\delta(Q_1, 1) = Q_1$. Automa si sposta in $Q_1$.
    \item Legge '0': $\delta(Q_1, 0) = Q_0$. Automa si sposta in $Q_0$.
    \item Stringa terminata. L'automa si trova in $Q_0$, che non è uno stato accettante.
\end{enumerate}
Quindi, la stringa \texttt{110} (che rappresenta il numero 6, pari) è rifiutata.

Questo modello dimostra come un DFA possa "decidere" se una data stringa appartiene a un linguaggio specifico. I DFA sono un modello di calcolo molto semplice ma potente per la loro classe di problemi.

\end{document}