Acciones

Diferencia entre revisiones de «Razonamiento por casos y por inducci¢n»

De Lógica matemática y fundamentos (2018-19)

 
Línea 1: Línea 1:
 
<source lang="isabelle">
 
<source lang="isabelle">
chapter {* Tema 6: Razonamiento estructurado sobre programas *}
+
chapter {* Tema 7: Razonamiento por casos y por inducción *}
  
theory T6_Razonamiento_sobre_programas
+
theory T7_Razonamiento_por_casos_y_por_induccion
imports Main  
+
imports Main HOL.Parity
 
begin
 
begin
  
text {*  
+
text {*
   En este tema se demuestra con Isabelle las propiedades de los
+
   En este tema se amplían los métodos de demostración por casos y por
  programas funcionales como se expone en el tema 2a y se demostraron
+
   inducción iniciados en el tema anterior.
   automáticamente en el tema 2b. A diferencia del tema 2b, ahora
+
*}
  nos fijamos no sólo en el método de demostración sino en la estructura
 
  de la prueba resaltando su semejanza con las del tema 2a. *}
 
  
declare [[names_short]]
+
section {* Razonamiento por distinción de casos *}
  
section {* Razonamiento ecuacional *}
+
subsection {* Distinción de casos booleanos *}
  
text {* ----------------------------------------------------------------
+
text {*
   Ejemplo 1. Definir, por recursión, la función
+
   Ejemplo de demostración por distinción de casos booleanos:
    longitud :: 'a list ⇒ nat
+
   Demostrar "¬A ∨ A".
   tal que (longitud xs) es la longitud de la listas xs. Por ejemplo,
+
*}
    longitud [a,c,d] = 3
 
  ------------------------------------------------------------------- *}
 
  
fun longitud :: "'a list ⇒ nat" where
+
― ‹La demostración estructurada es›
   "longitud []    = 0"
+
lemma "¬A ∨ A"  
| "longitud (x#xs) = 1 + longitud xs"
+
proof cases
 
+
   assume "A"  
value "longitud [a,c,d] = 3"
+
  then show "¬A ∨ A" ..
 +
next
 +
  assume "¬A"  
 +
  then show "¬A ∨ A" ..
 +
qed
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 2. Demostrar que  
+
   Comentarios de la demostración anterior:
    longitud [a,c,d] = 3
+
  · "proof cases" indica que el método de demostración será por
   ------------------------------------------------------------------- *}
+
    distinción de casos.
 +
  · Se generan 2 casos:
 +
      1. ?P ⟹ ¬A ∨ A
 +
      2. ¬?P ⟹ ¬A ∨ A
 +
    donde ?P es una variable sobre las fórmulas.
 +
  · (assume "A") indica que se está usando "A" en lugar de la variable
 +
    ?P.
 +
  · "then" indica usando la fórmula anterior.
 +
  · ".." indica usando la regla lógica necesaria (las reglas lógicas se
 +
    estudiarán en los siguientes temas).
 +
   · "next" indica el siguiente caso (se puede observar cómo ha
 +
    sustituido ¬?P por ¬A.
 +
*}
  
lemma "longitud [a,c,d] = 3"
+
― ‹La demostración automática es›
by simp
+
lemma "¬A ∨ A"  
 +
by auto
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 3. Definir la función
+
   Ejemplo de demostración por distinción de casos booleanos con nombres:  
    fun intercambia :: 'a × 'b ⇒ 'b × 'a
+
   Demostrar "¬A ∨ A".
   tal que (intercambia p) es el par obtenido intercambiando las
+
*}
  componentes del par p. Por ejemplo,
 
    intercambia (u,v) = (v,u)
 
  ------------------------------------------------------------------ *}
 
  
fun intercambia :: "'a × 'b ⇒ 'b × 'a" where
+
― ‹La demostración estructurada es›
   "intercambia (x,y) = (y,x)"
+
lemma "¬A ∨ A"
 
+
proof (cases "A")
value "intercambia (u,v) = (v,u)"
+
   case True
 +
  then show "¬A ∨ A" ..
 +
next
 +
  case False
 +
  thus "¬A ∨ A" ..
 +
qed
  
 
text {*
 
text {*
   La definición de la función intercambia genera una regla de
+
   Comentarios sobre la demostración anterior:
   simplificación
+
  · (cases "A") indica que la demostración se hará por casos según los
   · intercambia.simps: intercambia (x,y) = (y,x)
+
    distintos valores de "A".
 
+
  · Como "A" es una fórmula, sus posibles valores son verdadero o falso.
   Se puede ver con  
+
   · "case True" indica que se está suponiendo que A es verdadera. Es
  · thm intercambia.simps
+
    equivalente a "assume A".
 +
   · "case False" indica que se está suponiendo que A es falsa. Es
 +
    equivalente a "assume ¬A".
 +
  · En general,  
 +
    · el método (cases F) es una abreviatura de la aplicación de la regla
 +
        ⟦F ⟹ Q; ¬F ⟹ Q⟧ ⟹ Q 
 +
    · La expresión "case True" es una abreviatura de F.
 +
    · La expresión "case False" es una abreviatura de ¬F.
 +
   · Ventajas de "cases" con nombre:
 +
    · reduce la escritura de la fórmula y
 +
    · es independiente del orden de los casos.
 
*}
 
*}
  
text {* ---------------------------------------------------------------
+
subsection {* Distinción de casos sobre otros tipos de datos *}
  Ejemplo 4. (p.6) Demostrar que
 
    intercambia (intercambia (x,y)) = (x,y)
 
  ------------------------------------------------------------------- *}
 
  
(* Demostración aplicativa *)
+
text {*
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
  Ejemplo de distinción de casos sobre listas:
   apply (simp only: intercambia.simps)
+
  Demostrar que la longitud del resto de una lista es la longitud de la
  done
+
   lista menos 1.  
 +
*}
  
(* Demostración declarativa *)
+
― ‹La demostración detallada es›
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
lemma "length (tl xs) = length xs - 1"
proof -
+
proof (cases xs)
   have "intercambia (intercambia (x,y)) = intercambia (y,x)"
+
  assume "xs = []"
    by (simp only: intercambia.simps)
+
   then show "length (tl xs) = length xs - 1" by simp
   also have "... = (x,y)"  
+
next
    by (simp only: intercambia.simps)
+
  fix y ys
   finally show "intercambia (intercambia (x,y)) = (x,y)"  
+
   assume "xs = y#ys"
    by simp
+
   then show "length(tl xs) = length xs - 1" by simp  
 
qed
 
qed
  
 
text {*
 
text {*
   Notas sobre el lenguaje: En la demostración anterior se ha usado
+
   Comentarios sobre la demostración anterior:
   · "proof" para iniciar la prueba,
+
   · "(cases xs)" indica que la demostración se hará por casos sobre los
  · "-" (después de "proof") para no usar el método por defecto,
+
     posibles valores de xs.
  · "have" para establecer un paso,
+
   · Como xs es una lista, sus posibles valores son la lista vacía ([]) o
  · "by (simp only: intercambia.simps)" para indicar que sólo se usa
+
     una lista no vacía (de la forma (y#ys)).
     como regla de escritura la correspondiente a la definición de
+
   · Se generan 2 casos:
    intercambia,
+
      1. xs = [] ⟹ length (tl xs) = length xs - 1
   · "also" para encadenar pasos ecuacionales,
+
      2. ⋀a list. xs = a # list ⟹ length (tl xs) = length xs - 1
  · "..." para representar la derecha de la igualdad anterior en un
 
     razonamiento ecuacional,
 
  · "finally" para indicar el último pasa de un razonamiento ecuacional,
 
  · "show" para establecer la conclusión.
 
   · "by simp" para indicar el método de demostración por simplificación y
 
  · "qed" para terminar la pruebas,
 
 
*}
 
*}
  
(* Demostración declarativa simplificada *)
+
― ‹La demostración simplificada es›
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
lemma "length (tl xs) = length xs - 1"
proof -
+
proof (cases xs)
   have "intercambia (intercambia (x,y)) = intercambia (y,x)"  by simp
+
  case Nil
   also have "... = (x,y)" by simp
+
   then show ?thesis by simp
   finally show "intercambia (intercambia (x,y)) = (x,y)" by simp
+
next
 +
   case Cons
 +
   then show ?thesis by simp  
 
qed
 
qed
  
 
text {*
 
text {*
   Nota: La diferencia entre las dos demostraciones es que en los dos
+
   Comentarios sobre la dmostración anterior:
   primeros pasos no se explicita la regla de simplificación.
+
  · "case Nil" es una abreviatura de
 +
      "assume xs =[]".
 +
   · "case Cons" es una abreviatura de
 +
      "fix y ys assume xs = y#ys"
 +
  · ?thesis es una abreviatura de la conclusión del lema.
 
*}
 
*}
  
(* Demostración automática *)
+
― ‹La demostración automática es›
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
lemma "length (tl xs) = length xs - 1"  
  by simp
+
by auto
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 5. Definir, por recursión, la función
+
   En el siguiente ejemplo vamos a demostrar una propiedad de la función
    inversa :: 'a list ⇒ 'a list
+
   drop que está definida en la teoría List de forma que (drop n xs) la
   tal que (inversa xs) es la lista obtenida invirtiendo el orden de los
+
  lista obtenida eliminando en xs} los n primeros elementos. Su
  elementos de xs. Por ejemplo,
+
   definición es la siguiente 
    inversa [a,d,c] = [c,d,a]
+
    drop_Nil: "drop n []    = []"  
   ------------------------------------------------------------------ *}
+
    drop_Cons: "drop n (x#xs) = (case n of
 
+
                                    0 => x#xs |
fun inversa :: "'a list ⇒ 'a list" where
+
                                    Suc(m) => drop m xs)"
  "inversa []    = []"
+
*}
| "inversa (x#xs) = inversa xs @ [x]"
 
 
 
value "inversa [a,d,c] = [c,d,a]"
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 6. (p. 9) Demostrar que
 
    inversa [x] = [x]
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración aplicativa es *)
 
lemma "inversa [x] = [x]"
 
  apply simp
 
  done
 
  
 
text {*
 
text {*
   En la demostración anterior se usaron las siguientes reglas:
+
   Ejemplo de análisis de casos:
   · inversa.simps(1): inversa [] = []
+
   Demostrar que el resultado de eliminar los n+1 primeros elementos de
   · inversa.simps(2): inversa (x#xs) = inversa xs @ [x]
+
   xs es el mismo que eliminar los n primeros elementos del resto de xs.
  · append_Nil:      [] @ ys = ys
 
  Vamos a explicitar su aplicación.
 
 
*}
 
*}
 
 
(* La demostración aplicativa detallada es *)
 
lemma "inversa [x] = [x]"
 
  apply (simp only: inversa.simps(2))
 
  apply (simp only: inversa.simps(1))
 
  apply (simp only: append_Nil)
 
  done
 
  
(* La demostración declarativa es *)
+
― ‹La demostración detallada es›
lemma "inversa [x] = [x]"
+
lemma "drop (n + 1) xs = drop n (tl xs)"
proof -
+
proof (cases xs)
  have "inversa [x] = inversa (x#[])" by simp
+
   case Nil
  also have "... = (inversa []) @ [x]" by (simp only: inversa.simps(2))
+
   then show ?thesis by simp
  also have "... = [] @ [x]" by (simp only: inversa.simps(1))
+
next
   also have "... = [x]" by (simp only: append_Nil)
+
   case Cons
   finally show "inversa [x] = [x]" by simp
+
   then show ?thesis by simp
qed
 
 
 
(* La demostración declarativa simplificada es *)
 
lemma "inversa [x] = [x]"
 
proof -
 
  have "inversa [x] = inversa (x#[])" by simp
 
  also have "... = (inversa []) @ [x]" by simp
 
  also have "... = [] @ [x]" by simp
 
   also have "... = [x]" by simp
 
   finally show "inversa [x] = [x]" by simp
 
 
qed
 
qed
  
(* La demostración automática es *)
+
― ‹La demostración automática es›
lemma "inversa [x] = [x]"
+
lemma "drop (n + 1) xs = drop n (tl xs)"
  by simp
+
by (cases xs) auto
  
section {* Razonamiento por inducción sobre los naturales *}
+
section {* Inducción matemática *}
  
 
text {*
 
text {*
   [Principio de inducción sobre los naturales] Para demostrar una
+
   [Principio de inducción matemática]
  propiedad P para todos los números naturales basta probar que el 0
+
  Para demostrar una propiedad P para todos los números naturales basta
  tiene la propiedad P y que si n tiene la propiedad P, entonces n+1
+
  probar que el 0 tiene la propiedad P y que si n tiene la propiedad P,
  también la tiene.
+
  entonces n+1 también la tiene.  
 
     ⟦P 0; ⋀n. P n ⟹ P (Suc n)⟧ ⟹ P m
 
     ⟦P 0; ⋀n. P n ⟹ P (Suc n)⟧ ⟹ P m
  
   En Isabelle el principio de inducción sobre los naturales está
+
   En Isabelle el principio de inducción matemática está formalizado en
   formalizado en el teorema nat.induct y puede verse con
+
   el teorema nat.induct y puede verse con
 
     thm nat.induct
 
     thm nat.induct
 
*}
 
*}
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 7. Definir la función
+
   Ejemplo de demostración por inducción: Usaremos el principio de
    repite :: nat ⇒ 'a ⇒ 'a list
+
  inducción matemática para demostrar que
   tal que (repite n x) es la lista formada por n copias del elemento
+
    1 + 3 + ... + (2n-1) = n^2
  x. Por ejemplo,  
+
 
     repite 3 a = [a,a,a]
+
  Definición. [Suma de los primeros impares]
  ------------------------------------------------------------------ *}
+
   (suma_impares n) la suma de los n números impares. Por ejemplo,
 +
     suma_impares 3 = 9
 +
*}
  
fun repite :: "nat ⇒ 'a ⇒ 'a list" where
+
fun suma_impares :: "nat ⇒ nat" where
   "repite 0 x      = []"
+
   "suma_impares 0 = 0"  
| "repite (Suc n) x = x # (repite n x)"
+
| "suma_impares (Suc n) = (2*(Suc n) - 1) + suma_impares n"
  
value "repite 3 a = [a,a,a]"
+
value "suma_impares 3"
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 8. (p. 18) Demostrar que  
+
   Ejemplo de demostración por inducción matemática:
    longitud (repite n x) = n
+
  Demostrar que la suma de los n primeros números impares es n^2.
  ------------------------------------------------------------------- *}
+
*}
  
(* La demostración aplicativa es *)
+
― ‹Demostración del lema anterior por inducción y razonamiento
lemma "longitud (repite n x) = n"
+
  ecuacional›
   apply (induct n)
+
lemma "suma_impares n = n * n"
  apply simp_all
+
proof (induct n)
   done
+
  show "suma_impares 0 = 0 * 0" by simp
 +
next
 +
  fix n assume HI: "suma_impares n = n * n"
 +
  have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n"  
 +
    by simp
 +
   also have "… = (2 * (Suc n) - 1) + n * n" using HI by simp
 +
  also have "… = n * n + 2 * n + 1" by simp
 +
   finally show "suma_impares (Suc n) = (Suc n) * (Suc n)" by simp
 +
qed
  
(* La demostración estructurada es *)
+
― ‹Demostración del lema anterior con patrones y razonamiento
lemma "longitud (repite n x) = n"
+
  ecuacional›
 +
lemma "suma_impares n = n * n" (is "?P n")
 
proof (induct n)
 
proof (induct n)
   show "longitud (repite 0 x) = 0" by simp
+
   show "?P 0" by simp
next  
+
next
   fix n
+
   fix n  
   assume HI: "longitud (repite n x) = n"
+
   assume HI: "?P n"
   have "longitud (repite (Suc n) x) = longitud (x # (repite n x))"  
+
   have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n"  
 
     by simp
 
     by simp
   also have "... = 1 + longitud (repite n x)" by simp
+
   also have "= (2 * (Suc n) - 1) + n * n" using HI by simp
   also have "... = 1 + n" using HI by simp
+
   also have "= n * n + 2 * n + 1" by simp
   finally show "longitud (repite (Suc n) x) = Suc n" by simp
+
   finally show "?P (Suc n)" by simp
 
qed
 
qed
  
 
text {*
 
text {*
   Comentarios sobre la demostración anterior:
+
   Comentario sobre la demostración anterior:
   · A la derecha de proof se indica el método de la demostración.
+
   · Con la expresión
  · (induct n) indica que la demostración se hará por inducción en n.
+
      "suma_impares n = n * n" (is "?P n")
  · Se generan dos subobjetivos correspondientes a la base y el paso de
+
     se abrevia "suma_impares n = n * n" como "?P n". Por tanto,
    inducción:
+
      "?P 0"       es una abreviatura de "suma_impares 0 = 0 * 0"
    1. longitud (repite 0 x) = 0
+
      "?P (Suc n)" es una abreviatura de "suma_impares (Suc n) = (Suc n) * (Suc n)"
    2. ⋀n. longitud (repite n x) = n ⟹ longitud (repite (Suc n) x) = Suc n
+
   · En general, cualquier fórmula seguida de (is patrón) equipara el
     donde ⋀n se lee "para todo n"
+
    patrón con la fórmula.  
  · "next" indica el siguiente subobjetivo.
 
  · "fix n" indica "sea n un número natural cualquiera"
 
  · assume HI: "longitud (repite n x) = n" indica «supongamos que
 
    "longitud (repite n x) = n" y sea HI la etiqueta de este supuesto».
 
   · "using HI" usando la propiedad etiquetada con HI.  
 
 
*}
 
*}
  
(* La demostración automática es *)
+
― ‹La demostración usando patrones es›
lemma "longitud (repite n x) = n"
+
lemma "suma_impares n = n * n" (is "?P n")
  by (induct n) auto
+
proof (induct n)
 +
  show "?P 0" by simp
 +
next
 +
  fix n
 +
  assume "?P n"
 +
  then show "?P (Suc n)" by simp
 +
qed
 +
 
 +
― ‹La demostración automática es›
 +
lemma "suma_impares n = n * n"
 +
by (induct n) auto
  
section {* Razonamiento por inducción sobre listas *}
+
text {*  
 +
  Ejemplo de definición con existenciales.
 +
  Un número natural n es par si existe un natural m tal que n=m+m. 
 +
*}
  
text {*
+
definition par :: "nat ⇒ bool" where
   Para demostrar una propiedad para todas las listas basta demostrar
+
   "par n ≡ ∃m. n=m+m"
  que la lista vacía tiene la propiedad y que al añadir un elemento a
 
  una lista que tiene la propiedad se obtiene otra lista que también
 
  tiene la propiedad.  
 
  
   En Isabelle el principio de inducción sobre listas está formalizado
+
text {*
   mediante el teorema list.induct
+
   Ejemplo de inducción y existenciales:
    ⟦P [];
+
   Demostrar que para todo número natural n, se verifica que n*(n+1) par.
      ⋀x xs. P xs ⟹ P (x#xs)
 
    ⟹ P xs
 
 
*}
 
*}
  
text {* ---------------------------------------------------------------
+
― ‹Demostración detallada por inducción›
   Ejemplo 9. Definir la función
+
lemma
    conc :: 'a list ⇒ 'a list ⇒ 'a list
+
  fixes n :: "nat"
   tal que (conc xs ys) es la concatención de las listas xs e ys. Por
+
  shows "par (n*(n+1))"
   ejemplo,
+
proof (induct n)
    conc [a,d] [b,d,a,c] = [a,d,b,d,a,c]
+
   show "par (0*(0+1))" by (simp add: par_def)
   ------------------------------------------------------------------ *}
+
next
 +
  fix n
 +
  assume "par (n*(n+1))"
 +
  then have "∃m. n*(n+1) = m+m" by (simp add:par_def)
 +
   then obtain m where m: "n*(n+1) = m+m" ..
 +
   then have "(Suc n)*((Suc n)+1) = (m+n+1)+(m+n+1)" by auto
 +
  then have "∃m. (Suc n)*((Suc n)+1) = m+m" ..
 +
   then show "par ((Suc n)*((Suc n)+1))" by (simp add:par_def)
 +
qed
  
fun conc :: "'a list ⇒ 'a list ⇒ 'a list" where
+
text {*
  "conc []    ys = ys"
+
  Comentarios sobre la demostración anterior:
| "conc (x#xs) ys = x # (conc xs ys)"
+
  · (fixes n :: "nat") es una abreviatura de "sea n un número natural".
 +
*}
  
value "conc [a,d] [b,d,a,c] = [a,d,b,d,a,c]"
+
text {*
 +
  En Isabelle puede demostrarse de manera más simple un lema equivalente
 +
  usando en lugar de la función "par" la función "even" definida en la
 +
  teoría Parity por
 +
    even x ⟷ x mod 2 = 0"
 +
*}
  
text {* ---------------------------------------------------------------
+
lemma
   Ejemplo 10. (p. 24) Demostrar que
+
  fixes n :: "nat"
    conc xs (conc ys zs) = (conc xs ys) zs
+
   shows "even (n*(n+1))"
  ------------------------------------------------------------------- *}
+
by auto
  
(* La demostración estructurada es *)
+
text {*
lemma "conc xs (conc ys zs) = conc (conc xs ys) zs"
+
   Comentarios sobre la demostración anterior:
proof (induct xs)
+
   · Para poder usar la función "even" de la librería Parity es necesario
   show "conc [] (conc ys zs) = conc (conc [] ys) zs" by simp
+
    importar dicha librería. Por ello, antes del inicio de la teoría
next
+
    aparece
  fix x xs
+
      imports Main Parity
  assume HI: "conc xs (conc ys zs) = conc (conc xs ys) zs"
+
*}
   have "conc (x # xs) (conc ys zs) = x # (conc xs (conc ys zs))" by simp
 
  also have "... = x # (conc (conc xs ys) zs)" using HI by simp
 
  also have "... = conc (conc (x # xs) ys) zs" by simp
 
  finally show "conc (x # xs) (conc ys zs) = conc (conc (x # xs) ys) zs"
 
    by simp
 
qed
 
  
 
text {*
 
text {*
   Comentario sobre la demostración anterior
+
   Para completar la demostración basta demostrar la equivalencia de las
   · (induct xs) genera dos subobjetivos:
+
   funciones "par" y "even".  
    1. conc [] (conc ys zs) = conc (conc [] ys) zs
 
    2. ⋀a xs. conc xs (conc ys zs) = conc (conc xs ys) zs ⟹
 
              conc (a#xs) (conc ys zs) = conc (conc (a#xs) ys) zs
 
 
*}
 
*}
  
(* La demostración automática es *)
+
lemma
lemma "conc xs (conc ys zs) = conc (conc xs ys) zs"
+
  fixes n :: "nat"
   by (induct xs) auto
+
  shows "par n = even n"
 +
proof -
 +
  have "par n = (∃m. n = m+m)" by (simp add:par_def)
 +
   then show "par n = even n" by presburger
 +
qed
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 11. Refutar que  
+
   Comentarios sobre la demostración anterior:
    conc xs ys = conc ys xs
+
  · "by presburger" indica que se use como método de demostración el
  ------------------------------------------------------------------- *}
+
    algoritmo de decisión de la aritmética de Presburger.
 +
*}
  
lemma "conc xs ys = conc ys xs"
+
section {* Inducción estructural *}
  quickcheck
 
  oops
 
  
text {* Encuentra el contraejemplo,
+
text {*
   xs = [a2]
+
  Inducción estructural:
   ys = [a1] *}
+
  · En Isabelle puede hacerse inducción estructural sobre cualquier tipo
 +
    recursivo.
 +
  · La inducción matemática es la inducción estructural sobre el tipo de
 +
    los naturales.
 +
   · El esquema de inducción estructural sobre listas es
 +
    · list.induct: ⟦P []; ⋀x ys. P ys ⟹ P (x # ys)⟧ ⟹ P zs
 +
   · Para demostrar una propiedad para todas las listas basta demostrar
 +
    que la lista vacía tiene la propiedad y que al añadir un elemento a una
 +
    lista que tiene la propiedad se obtiene una lista que también tiene la
 +
    propiedad.
 +
  · En Isabelle el principio de inducción sobre listas está formalizado
 +
    mediante el teorema list.induct que puede verse con
 +
      thm list.induct
 +
*}
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 12. (p. 28) Demostrar que
+
   Concatenación de listas:
     conc xs [] = xs
+
  En la teoría List.thy está definida la concatenación de listas (que
  ------------------------------------------------------------------- *}
+
  se representa por @) como sigue
 +
     append_Nil:  "[]@ys    = ys"
 +
    append_Cons: "(x#xs)@ys = x#(xs@ys)"
 +
*}
  
(* La demostración estructurada es *)
+
text {*
lemma "conc xs [] = xs"
+
   Lema. [Ejemplo de inducción sobre listas]
proof (induct xs)
+
   Demostrar que la concatenación de listas es asociativa.
   show "conc [] [] = []" by simp
+
*}
next
 
  fix x xs
 
  assume HI: "conc xs [] = xs"
 
  have "conc (x # xs) [] = x # (conc xs [])" by simp
 
  also have "... = x # xs" using HI by simp
 
  finally show "conc (x # xs) [] = x # xs" by simp
 
qed
 
 
 
(* La demostración automática es *)
 
lemma "conc xs [] = xs"
 
  by (induct xs) simp_all
 
 
 
text {* ---------------------------------------------------------------
 
   Ejemplo 13. (p. 30) Demostrar que  
 
    longitud (conc xs ys) = longitud xs + longitud ys
 
  ------------------------------------------------------------------- *}
 
  
(* La demostración estructurada es *)
+
― ‹La demostración estructurada es›
lemma "longitud (conc xs ys) = longitud xs + longitud ys"
+
lemma conc_asociativa: "xs @ (ys @ zs) = (xs @ ys) @ zs"
 
proof (induct xs)
 
proof (induct xs)
   show "longitud (conc [] ys) = longitud [] + longitud ys" by simp
+
   show "[] @ (ys @ zs) = ([] @ ys) @ zs"
 +
  proof -
 +
    have "[] @ (ys @ zs) = ys @ zs" by simp
 +
    also have "… = ([] @ ys) @ zs" by simp
 +
    finally show ?thesis .
 +
  qed
 
next
 
next
 
   fix x xs
 
   fix x xs
   assume HI: "longitud (conc xs ys) = longitud xs + longitud ys"
+
   assume HI: "xs @ (ys @ zs) = (xs @ ys) @ zs"
   have "longitud (conc (x # xs) ys) = longitud (x # (conc xs ys))"  
+
   show "(x#xs) @ (ys @ zs) = ((x#xs) @ ys) @ zs"
     by simp
+
  proof -
  also have "... = 1 + longitud (conc xs ys)" by simp
+
     have "(x#xs) @ (ys @ zs) = x#(xs @ (ys @ zs))" by simp
  also have "... = 1 + longitud xs + longitud ys" using HI by simp
+
    also have "= x#((xs @ ys) @ zs)" using HI by simp
  also have "... = longitud (x # xs) + longitud ys" by simp
+
    also have "= (x#(xs @ ys)) @ zs" by simp
  finally show "longitud (conc (x # xs) ys) =
+
    also have "… = ((x#xs) @ ys) @ zs" by simp
                longitud (x # xs) + longitud ys" by simp
+
    finally show ?thesis .
 +
  qed
 
qed
 
qed
  
(* La demostración automática es *)
+
― ‹La demostración automática es›
lemma "longitud (conc xs ys) = longitud xs + longitud ys"
+
lemma conc_asociativa_1: "xs @ (ys @ zs) = (xs @ ys) @ zs"
  by (induct xs) auto
+
by (induct xs) auto
  
section {* Inducción correspondiente a la definición recursiva *}
+
section {* Heurísticas para la inducción *}
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 14. Definir la función
+
   Definición. [Definición recursiva de inversa]
    coge :: nat ⇒ 'a list ⇒ 'a list
+
   (inversa xs) la inversa de la lista xs. Por ejemplo,
   tal que (coge n xs) es la lista de los n primeros elementos de xs. Por  
+
     inversa [a,b,c] = [c,b,a]  
  ejemplo,  
+
*}
     coge 2 [a,c,d,b,e] = [a,c]
 
  ------------------------------------------------------------------ *}
 
  
fun coge :: "nat ⇒ 'a list ⇒ 'a list" where
+
fun inversa :: "'a list ⇒ 'a list" where
   "coge n []          = []"
+
   "inversa [] = []"  
| "coge 0 xs          = []"
+
| "inversa (x#xs) = (inversa xs) @ [x]"
| "coge (Suc n) (x#xs) = x # (coge n xs)"
 
 
 
value "coge 2 [a,c,d,b,e] = [a,c]"
 
  
text {* ---------------------------------------------------------------
+
value "inversa [a,b,c]"
  Ejemplo 15. Definir la función
 
    elimina :: nat ⇒ 'a list ⇒ 'a list
 
  tal que (elimina n xs) es la lista obtenida eliminando los n primeros
 
  elementos de xs. Por ejemplo,
 
    elimina 2 [a,c,d,b,e] = [d,b,e]
 
  ------------------------------------------------------------------ *}
 
 
 
fun elimina :: "nat ⇒ 'a list ⇒ 'a list" where
 
  "elimina n []          = []"
 
| "elimina 0 xs          = xs"
 
| "elimina (Suc n) (x#xs) = elimina n xs"
 
 
 
value "elimina 2 [a,c,d,b,e] = [d,b,e]"
 
  
 
text {*  
 
text {*  
   La definición coge genera el esquema de inducción coge.induct:
+
   Definición. [Definición de inversa con acumuladores]
    ⟦⋀n. P n [];
+
  (inversaAc xs) es la inversa de la lista xs calculada con
      ⋀x xs. P 0 (x#xs);
+
   acumuladores. Por ejemplo,
      ⋀n x xs. P n xs ⟹ P (Suc n) (x#xs)⟧
+
     inversaAc [a,b,c]       = [c,b,a]  
    ⟹ P n x
+
    inversaAcAux [a,b,c] [] = [c,b,a]  
 
 
   Puede verse usando "thm coge.induct". *}
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 16. (p. 35) Demostrar que
 
     conc (coge n xs) (elimina n xs) = xs
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "conc (coge n xs) (elimina n xs) = xs"
 
proof (induct rule: coge.induct)
 
  fix n
 
  show "conc (coge n []) (elimina n []) = []" by simp
 
next
 
  fix x xs
 
  show "conc (coge 0 (x#xs)) (elimina 0 (x#xs)) = x#xs" by simp
 
next
 
  fix n x xs
 
  assume HI: "conc (coge n xs) (elimina n xs) = xs"
 
  have "conc (coge (Suc n) (x#xs)) (elimina (Suc n) (x#xs)) =
 
        conc (x#(coge n xs)) (elimina n xs)" by simp
 
  also have "... = x#(conc (coge n xs) (elimina n xs))" by simp
 
  also have "... = x#xs" using HI by simp 
 
  finally show "conc (coge (Suc n) (x#xs)) (elimina (Suc n) (x#xs)) =
 
                x#xs"
 
    by simp
 
qed
 
 
 
text {*
 
  Comentario sobre la demostración anterior:
 
  · (induct rule: coge.induct) indica que el método de demostración es
 
    por el esquema de inducción correspondiente a la definición de la
 
    función coge.
 
  · Se generan 3 subobjetivos:
 
    · 1. ⋀n. conc (coge n []) (elimina n []) = []
 
    · 2. ⋀x xs. conc (coge 0 (x#xs)) (elimina 0 (x#xs)) = x#xs
 
    · 3. ⋀n x xs.
 
            conc (coge n xs) (elimina n xs) = xs ⟹
 
            conc (coge (Suc n) (x#xs)) (elimina (Suc n) (x#xs)) = x#xs
 
 
*}
 
*}
  
(* La demostración automática es *)
+
fun inversaAcAux :: "'a list ⇒ 'a list ⇒ 'a list" where
lemma "conc (coge n xs) (elimina n xs) = xs"
+
  "inversaAcAux [] ys    = ys"
  by (induct rule: coge.induct) auto
+
| "inversaAcAux (x#xs) ys = inversaAcAux xs (x#ys)"
  
section {* Razonamiento por casos *}
+
definition inversaAc :: "'a list ⇒ 'a list" where
 +
  "inversaAc xs ≡ inversaAcAux xs []"
  
text {* ---------------------------------------------------------------
+
value "inversaAcAux [a,b,c] []"
  Ejemplo 17. Definir la función
+
value "inversaAc [a,b,c]"
    esVacia :: 'a list ⇒ bool
 
  tal que (esVacia xs) se verifica si xs es la lista vacía. Por ejemplo,
 
    esVacia [] = True
 
    esVacia [1] = False
 
  ------------------------------------------------------------------ *}
 
  
fun esVacia :: "'a list ⇒ bool" where
+
text {*  
  "esVacia []    = True"
+
   Lema. [Ejemplo de equivalencia entre las definiciones]
| "esVacia (x#xs) = False"
+
   La inversa de [a,b,c] es lo mismo calculada con la primera definición
 
+
   que con la segunda.
value "esVacia []  = True"
 
value "esVacia [a] = False"
 
 
 
text {* ---------------------------------------------------------------
 
   Ejemplo 18 (p. 39) . Demostrar que
 
    esVacia xs = esVacia (conc xs xs)
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "esVacia xs = esVacia (conc xs xs)"
 
proof (cases xs)
 
  assume "xs = []"
 
   then show "esVacia xs = esVacia (conc xs xs)" by simp
 
next
 
  fix y ys
 
  assume "xs = y#ys"
 
  then show "esVacia xs = esVacia (conc xs xs)" by simp
 
qed
 
 
 
text {*
 
  Comentarios sobre la demostración anterior:
 
  · "(cases xs)" es el método de demostración por casos según xs.
 
  · Se generan dos subobjetivos  correspondientes a los dos
 
    constructores de listas:
 
    · 1. xs = [] ⟹ esVacia xs = esVacia (conc xs xs)
 
    · 2. ⋀y ys. xs = y#ys ⟹ esVacia xs = esVacia (conc xs xs)
 
   · "then" indica "usando la propiedad anterior"
 
 
*}
 
*}
  
(* La demostración estructurada simplificada es *)
+
lemma "inversaAc [a,b,c] = inversa [a,b,c]"
lemma "esVacia xs = esVacia (conc xs xs)"
+
by (simp add: inversaAc_def)
proof (cases xs)
 
  case Nil
 
  then show "esVacia xs = esVacia (conc xs xs)" by simp
 
next
 
  case Cons
 
  then show "esVacia xs = esVacia (conc xs xs)" by simp
 
qed
 
  
 
text {*
 
text {*
   Comentarios sobre la demostración anterior:
+
   Nota. [Ejemplo fallido de demostración por inducción]
   · "case Nil" es una abreviatura de "assume xs = []"
+
   El siguiente intento de demostrar que para cualquier lista xs, se
   · "case Cons" es una abreviatura de "fix y ys assume xs = y#ys"
+
   tiene que  "inversaAc xs = inversa xs" falla.
  · "thus" es una abreviatura de "then show".
 
 
*}
 
*}
  
(* La demostración con el patrón sugerido es *)
+
lemma "inversaAc xs = inversa xs"
lemma "esVacia xs = esVacia (conc xs xs)"
+
proof (induct xs)
proof (cases xs)
+
   show "inversaAc [] = inversa []" by (simp add: inversaAc_def)
   case Nil
 
  then show ?thesis by simp
 
 
next
 
next
   case (Cons x xs)
+
   fix a xs assume HI: "inversaAc xs = inversa xs"
  then show ?thesis by simp
+
  have "inversaAc (a#xs) = inversaAcAux (a#xs) []"
qed
+
    by (simp add: inversaAc_def)
 
+
  also have "… = inversaAcAux xs [a]" by simp
(* La demostración automática es *)
+
  also have "… = inversa (a#xs)"
lemma "esVacia xs = esVacia (conc xs xs)"
+
   (* Problema: la hipótesis de inducción no es aplicable. *)
   by (cases xs) auto
+
oops
 
 
section {* Heurística de generalización *}
 
  
 
text {*  
 
text {*  
   Heurística de generalización: Cuando se use demostración estructural,
+
   Nota. [Heurística de generalización]
  cuantificar universalmente las variables libres (o, equivalentemente,
+
  Cuando se use demostración estructural, cuantificar universalmente las  
  considerar las variables libres como variables arbitrarias). *}
+
  variables libres (o, equivalentemente, considerar las variables libres
 
+
  como variables arbitrarias).
text {* ---------------------------------------------------------------
 
  Ejemplo 19. Definir la función
 
    inversaAc :: 'a list ⇒ 'a list
 
  tal que (inversaAc xs) es a inversa de xs calculada usando
 
  acumuladores. Por ejemplo,
 
    inversaAc [a,c,b,e] = [e,b,c,a]
 
  ------------------------------------------------------------------ *}
 
 
 
fun inversaAcAux :: "'a list ⇒ 'a list ⇒ 'a list" where
 
  "inversaAcAux [] ys    = ys"
 
| "inversaAcAux (x#xs) ys = inversaAcAux xs (x#ys)"
 
  
fun inversaAc :: "'a list ⇒ 'a list" where
+
   Lema. [Lema con generalización]
   "inversaAc xs = inversaAcAux xs []"
+
   Para toda lista ys se tiene
 
 
value "inversaAc [a,c,b,e] = [e,b,c,a]"
 
 
 
text {* ---------------------------------------------------------------
 
   Ejemplo 20. (p. 44) Demostrar que
 
 
     inversaAcAux xs ys = (inversa xs) @ ys
 
     inversaAcAux xs ys = (inversa xs) @ ys
  ------------------------------------------------------------------- *}
+
*}
  
(* La demostración estructurada es *)
+
― ‹La demostración estructurada es›
 
lemma inversaAcAux_es_inversa:
 
lemma inversaAcAux_es_inversa:
   "inversaAcAux xs ys = (inversa xs) @ ys"
+
   "inversaAcAux xs ys = (inversa xs)@ys"
 
proof (induct xs arbitrary: ys)
 
proof (induct xs arbitrary: ys)
   show "⋀ys. inversaAcAux [] ys = inversa [] @ ys" by simp
+
   show "⋀ys. inversaAcAux [] ys = (inversa [])@ys" by simp
 
next
 
next
 
   fix a xs  
 
   fix a xs  
Línea 569: Línea 472:
 
     have "inversaAcAux (a#xs) ys = inversaAcAux xs (a#ys)" by simp
 
     have "inversaAcAux (a#xs) ys = inversaAcAux xs (a#ys)" by simp
 
     also have "… = inversa xs@(a#ys)" using HI by simp
 
     also have "… = inversa xs@(a#ys)" using HI by simp
     also have "… = inversa (a#xs)@ys" by simp  
+
     also have "… = inversa (a#xs)@ys" using [[simp_trace]] by simp  
 
     finally show "inversaAcAux (a#xs) ys = inversa (a#xs)@ys" by simp
 
     finally show "inversaAcAux (a#xs) ys = inversa (a#xs)@ys" by simp
 
   qed
 
   qed
 
qed
 
qed
 +
 +
― ‹La demostración automática es›
 +
lemma inversaAcAux_es_inversa_1:
 +
  "inversaAcAux xs ys = (inversa xs)@ys"
 +
by (induct xs arbitrary: ys) auto
  
 
text {*
 
text {*
   Comentarios sobre la demostración anterior:
+
   Corolario. Para cualquier lista xs, se tiene que
  · "(induct xs arbitrary: ys)" es el método de demostración por
+
    inversaAc xs = inversa xs
    inducción sobre xs usando ys como variable arbitraria.
 
  · Se generan dos subobjetivos:
 
    · 1. ⋀ys. inversaAcAux [] ys = inversa [] @ ys
 
    · 2. ⋀a xs ys. (⋀ys. inversaAcAux xs ys = inversa xs @ ys) ⟹
 
                    inversaAcAux (a # xs) ys = inversa (a # xs) @ ys
 
  · Dentro de una demostración se pueden incluir otras demostraciones.
 
  · Para demostrar la propiedad universal "⋀ys. P(ys)" se elige una
 
    lista arbitraria (con "fix ys") y se demuestra "P(ys)".
 
 
*}
 
*}
  
(* La demostración automática es *)
 
lemma "inversaAcAux xs ys = (inversa xs)@ys"
 
  by (induct xs arbitrary: ys) auto
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 21. (p. 43) Demostrar que
 
    inversaAc xs = inversa xs
 
  ------------------------------------------------------------------- *}
 
 
(* La demostración automática es *)
 
 
corollary "inversaAc xs = inversa xs"
 
corollary "inversaAc xs = inversa xs"
   by (simp add: inversaAcAux_es_inversa)
+
  using [[simp_trace]]
 +
   by (simp add: inversaAcAux_es_inversa inversaAc_def)
  
 
text {*
 
text {*
   Comentario de la demostración anterior:
+
   Nota. En el paso "inversa xs@(a#ys) = inversa (a#xs)@ys" se usan
   · "(simp add: inversaAcAux_es_inversa)" es el método de demostración
+
  lemas de la teoría List. Se puede observar, insertano
    por simplificación usando como regla de simplificación la propiedad
+
    using [[simp_trace]]
    inversaAcAux_es_inversa.  
+
  entre la igualdad y by simp, que los lemas usados son
 +
  · List.append_simps_1: []@ys = ys
 +
   · List.append_simps_2: (x#xs)@ys = x#(xs@ys)
 +
  · List.append_assoc:   (xs @ ys) @ zs = xs @ (ys @ zs)
 +
  Las dos primeras son las ecuaciones de la definición de append.
 +
 
 +
  En la siguiente demostración se detallan los lemas utilizados.
 
*}
 
*}
  
section {* Demostración por inducción para funciones de orden superior *}
+
lemma "(inversa xs)@(a#ys) = (inversa (a#xs))@ys"
 +
proof -
 +
  have "(inversa xs)@(a#ys) = (inversa xs)@(a#([]@ys))"
 +
    by (simp only: append.simps(1))
 +
  also have "… = (inversa xs)@([a]@ys)" by (simp only: append.simps(2))
 +
  also have "… = ((inversa xs)@[a])@ys" by (simp only: append_assoc)
 +
  also have "… = (inversa (a#xs))@ys" by (simp only: inversa.simps(2))
 +
  finally show ?thesis .
 +
qed
  
text {* ---------------------------------------------------------------
+
section {* Recursión general. La función de Ackermann *}
  Ejemplo 22. Definir la función
 
    sum :: nat list ⇒ nat
 
  tal que (sum xs) es la suma de los elementos de xs. Por ejemplo,
 
    sum [3,2,5] = 10
 
  ------------------------------------------------------------------ *}
 
  
fun sum :: "nat list ⇒ nat" where
+
text {*
   "sum []    = 0"
+
  El objetivo de esta sección es mostrar el uso de las definiciones
| "sum (x#xs) = x + sum xs"
+
   recursivas generales y sus esquemas de inducción. Como ejemplo se usa la
 +
  función de Ackermann (se puede consultar información sobre dicha función en
 +
  http://en.wikipedia.org/wiki/Ackermann_function).
  
value "sum [3,2,5] = 10"
+
  Definición.  La función de Ackermann se define por
 +
    A(m,n) = n+1,             si m=0,
 +
            A(m-1,1),        si m>0 y n=0,
 +
            A(m-1,A(m,n-1)), si m>0 y n>0
 +
  para todo los números naturales.
  
text {* ---------------------------------------------------------------
+
   La función de Ackermann es recursiva, pero no es primitiva recursiva.  
   Ejemplo 23. Definir la función
+
*}
    map :: ('a ⇒ 'b) ⇒ 'a list ⇒ 'b list
 
  tal que (map f xs) es la lista obtenida aplicando la función f a los
 
  elementos de xs. Por ejemplo,
 
    map (λx. 2*x) [3,2,5] = [6,4,10]
 
  ------------------------------------------------------------------ *}
 
  
fun map :: "('a ⇒ 'b) 'a list 'b list" where
+
fun ack :: "nat nat nat" where
   "map f []    = []"
+
   "ack 0      n      = n+1"
| "map f (x#xs) = (f x) # map f xs"
+
| "ack (Suc m) 0      = ack m 1"  
 +
| "ack (Suc m) (Suc n) = ack m (ack (Suc m) n)"
  
value "map (λx. 2*x) [3::nat,2,5] = [6,4,10]"
+
― ‹Ejemplo de evaluación›
 +
value "ack 2 3" (* devuelve 9 *)
  
text {* ---------------------------------------------------------------
+
text {*
   Ejemplo 24. (p. 45) Demostrar que
+
   Esquema de inducción correspondiente a una función:
    sum (map (λx. 2*x) xs) = 2 * (sum xs)
+
  · Al definir una función recursiva general se genera una regla de
  ------------------------------------------------------------------- *}
+
    inducción. En la definición anterior, la regla generada es
 +
    ack.induct:
 +
      ⟦⋀n. P 0 n;
 +
        ⋀m. P m 1 ⟹ P (Suc m) 0;
 +
        ⋀m n. ⟦P (Suc m) n; P m (ack (Suc m) n)⟧ ⟹ P (Suc m) (Suc n)
 +
      ⟹ P a b
 +
*}
  
(* La demostración estructurada es *)
+
text {*
lemma "sum (map (λx. 2*x) xs) = 2 * (sum xs)"
+
   Ejemplo de demostración por la inducción correspondiente a una función:
proof (induct xs)
+
   Demostrar que para todos m y n, A(m,n) > n.
  show "sum (map (λx. 2*x) []) = 2 * (sum [])" by simp
+
*}
next
 
   fix a xs
 
  assume HI: "sum (map (λx. 2*x) xs) = 2 * (sum xs)"
 
   have "sum (map (λx. 2*x) (a#xs)) = sum ((2*a)#(map (λx. 2*x) xs))"
 
    by simp
 
  also have "... = 2*a + sum (map (λx. 2*x) xs)" by simp
 
  also have "... = 2*a + 2*(sum xs)" using HI by simp
 
  also have "... = 2*(a + sum xs)" by simp
 
  also have "... = 2*(sum (a#xs))" by simp
 
  finally show "sum (map (λx. 2*x) (a#xs)) = 2*(sum (a#xs))" by simp
 
qed
 
  
(* La demostración automática es *)
+
― ‹La demostración detallada es›
lemma "sum (map (λx. 2*x) xs) = 2 * (sum xs)"
+
lemma "ack m n > n"
  by (induct xs) auto
+
proof (induct m n rule: ack.induct)
 
+
   fix n
text {* ---------------------------------------------------------------
+
   show "ack 0 n > n" by simp
  Ejemplo 25. (p. 48) Demostrar que
 
    longitud (map f xs) = longitud xs
 
   ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "longitud (map f xs) = longitud xs"
 
proof (induct xs)
 
   show "longitud (map f []) = longitud []" by simp
 
 
next
 
next
   fix a xs
+
   fix m
   assume HI: "longitud (map f xs) = longitud xs"
+
   assume "ack m 1 > 1"
   have "longitud (map f (a#xs)) = longitud (f a # (map f xs))" by simp
+
   then show "ack (Suc m) 0 > 0" by simp
   also have "... = 1 + longitud (map f xs)" by simp
+
next 
  also have "... = 1 + longitud xs" using HI by simp
+
  fix m n
  also have "... = longitud (a#xs)" by simp
+
   assume "n < ack (Suc m) n" and
   finally show "longitud (map f (a#xs)) = longitud (a#xs)" by simp
+
        "ack (Suc m) n < ack m (ack (Suc m) n)"
 +
   then show "Suc n < ack (Suc m) (Suc n)" by simp
 
qed
 
qed
 
(* La demostración automática es *)
 
lemma "longitud (map f xs) = longitud xs"
 
  by (induct xs) auto
 
 
section {* Referencias *}
 
  
 
text {*
 
text {*
   · J.A. Alonso. "Razonamiento sobre programas" http://goo.gl/R06O3
+
   Comentarios sobre la demostración anterior:
   · G. Hutton. "Programming in Haskell". Cap. 13 "Reasoning about
+
   · (induct m n rule: ack.induct) indica que el método de demostración
     programms".  
+
    es el esquema de recursión correspondiente a la definición de
   · S. Thompson. "Haskell: the Craft of Functional Programming, 3rd
+
     (ack m n).
     Edition. Cap. 8 "Reasoning about programms".  
+
   · Se generan 3 casos:
  · L. Paulson. "ML for the Working Programmer, 2nd Edition". Cap. 6.  
+
     1. ⋀n. n < ack 0 n
    "Reasoning about functional programs".
+
    2. ⋀m. 1 < ack m 1 ⟹ 0 < ack (Suc m) 0
 +
    3. ⋀m n. ⟦n < ack (Suc m) n;
 +
              ack (Suc m) n < ack m (ack (Suc m) n)⟧
 +
            ⟹ Suc n < ack (Suc m) (Suc n)
 
*}
 
*}
 +
 +
― ‹La demostración automática es›
 +
lemma "ack m n > n"
 +
by (induct m n rule: ack.induct) auto
  
 
end
 
end
 
</source>
 
</source>

Revisión del 13:06 7 feb 2019

chapter {* Tema 7: Razonamiento por casos y por inducción *}

theory T7_Razonamiento_por_casos_y_por_induccion
imports Main HOL.Parity
begin

text {*
  En este tema se amplían los métodos de demostración por casos y por
  inducción iniciados en el tema anterior.
*}

section {* Razonamiento por distinción de casos *}

subsection {* Distinción de casos booleanos *}

text {*
  Ejemplo de demostración por distinción de casos booleanos:
  Demostrar "¬A ∨ A".
*}

 La demostración estructurada es
lemma "¬A ∨ A" 
proof cases
  assume "A" 
  then show "¬A ∨ A" ..
next
  assume "¬A" 
  then show "¬A ∨ A" ..
qed

text {*
  Comentarios de la demostración anterior:
  · "proof cases" indica que el método de demostración será por
    distinción de casos. 
  · Se generan 2 casos:
       1. ?P ⟹ ¬A ∨ A
       2. ¬?P ⟹ ¬A ∨ A
    donde ?P es una variable sobre las fórmulas.
  · (assume "A") indica que se está usando "A" en lugar de la variable
    ?P.
  · "then" indica usando la fórmula anterior.
  · ".." indica usando la regla lógica necesaria (las reglas lógicas se
    estudiarán en los siguientes temas).
  · "next" indica el siguiente caso (se puede observar cómo ha
    sustituido ¬?P por ¬A.
*}

 La demostración automática es
lemma "¬A ∨ A" 
by auto

text {*
  Ejemplo de demostración por distinción de casos booleanos con nombres: 
  Demostrar "¬A ∨ A".
*}

 La demostración estructurada es
lemma "¬A ∨ A" 
proof (cases "A")
  case True 
  then show "¬A ∨ A" ..
next
  case False 
  thus "¬A ∨ A" .. 
qed

text {*
  Comentarios sobre la demostración anterior:
  · (cases "A") indica que la demostración se hará por casos según los
    distintos valores de "A".
  · Como "A" es una fórmula, sus posibles valores son verdadero o falso.
  · "case True" indica que se está suponiendo que A es verdadera. Es
    equivalente a "assume A".
  · "case False" indica que se está suponiendo que A es falsa. Es
    equivalente a "assume ¬A".
  · En general, 
    · el método (cases F) es una abreviatura de la aplicación de la regla
         ⟦F ⟹ Q; ¬F ⟹ Q⟧ ⟹ Q  
    · La expresión "case True" es una abreviatura de F.
    · La expresión "case False" es una abreviatura de ¬F.
  · Ventajas de "cases" con nombre: 
    · reduce la escritura de la fórmula y
    · es independiente del orden de los casos.
*}

subsection {* Distinción de casos sobre otros tipos de datos *}

text {*
  Ejemplo de distinción de casos sobre listas: 
  Demostrar que la longitud del resto de una lista es la longitud de la
  lista menos 1. 
*}

 La demostración detallada es
lemma "length (tl xs) = length xs - 1" 
proof (cases xs)
  assume "xs = []"
  then show "length (tl xs) = length xs - 1" by simp
next
  fix y ys
  assume "xs = y#ys"
  then show "length(tl xs) = length xs - 1" by simp 
qed

text {*
  Comentarios sobre la demostración anterior:
  · "(cases xs)" indica que la demostración se hará por casos sobre los
    posibles valores de xs.
  · Como xs es una lista, sus posibles valores son la lista vacía ([]) o
    una lista no vacía (de la forma (y#ys)).
  · Se generan 2 casos:
       1. xs = [] ⟹ length (tl xs) = length xs - 1
       2. ⋀a list. xs = a # list ⟹ length (tl xs) = length xs - 1
*}

 La demostración simplificada es 
lemma "length (tl xs) = length xs - 1" 
proof (cases xs)
  case Nil 
  then show ?thesis by simp
next
  case Cons 
  then show ?thesis by simp 
qed

text {*
  Comentarios sobre la dmostración anterior:
  · "case Nil" es una abreviatura de 
       "assume xs =[]".
  · "case Cons" es una abreviatura de 
       "fix y ys assume xs = y#ys"
  · ?thesis es una abreviatura de la conclusión del lema.
*}

 La demostración automática es 
lemma "length (tl xs) = length xs - 1" 
by auto

text {*
  En el siguiente ejemplo vamos a demostrar una propiedad de la función
  drop que está definida en la teoría List de forma que (drop n xs) la
  lista obtenida eliminando en xs} los n primeros elementos. Su
  definición es la siguiente   
     drop_Nil:  "drop n []     = []" 
     drop_Cons: "drop n (x#xs) = (case n of 
                                    0 => x#xs | 
                                    Suc(m) => drop m xs)"
*}

text {*
  Ejemplo de análisis de casos:
  Demostrar que el resultado de eliminar los n+1 primeros elementos de
  xs es el mismo que eliminar los n primeros elementos del resto de xs.  
*}

 La demostración detallada es
lemma "drop (n + 1) xs = drop n (tl xs)"
proof (cases xs)
  case Nil 
  then show ?thesis by simp
next
  case Cons 
  then show ?thesis by simp
qed

 La demostración automática es 
lemma "drop (n + 1) xs = drop n (tl xs)"
by (cases xs) auto

section {* Inducción matemática *}

text {*
  [Principio de inducción matemática]
  Para demostrar una propiedad P para todos los números naturales basta
  probar que el 0 tiene la propiedad P y que si n tiene la propiedad P,
  entonces n+1 también la tiene. 
     ⟦P 0; ⋀n. P n ⟹ P (Suc n)⟧ ⟹ P m

  En Isabelle el principio de inducción matemática está formalizado en
  el teorema nat.induct y puede verse con
     thm nat.induct
*}

text {*  
  Ejemplo de demostración por inducción: Usaremos el principio de
  inducción matemática para demostrar que 
     1 + 3 + ... + (2n-1) = n^2

  Definición. [Suma de los primeros impares] 
  (suma_impares n) la suma de los n números impares. Por ejemplo,
     suma_impares 3  =  9
*}

fun suma_impares :: "nat ⇒ nat" where
  "suma_impares 0 = 0" 
| "suma_impares (Suc n) = (2*(Suc n) - 1) + suma_impares n"

value "suma_impares 3"

text {*
  Ejemplo de demostración por inducción matemática:
  Demostrar que la suma de los n primeros números impares es n^2.
*}

 Demostración del lema anterior por inducción y razonamiento 
   ecuacional
lemma "suma_impares n = n * n"
proof (induct n)
  show "suma_impares 0 = 0 * 0" by simp
next
  fix n assume HI: "suma_impares n = n * n"
  have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n" 
    by simp
  also have "… = (2 * (Suc n) - 1) + n * n" using HI by simp
  also have "… = n * n + 2 * n + 1" by simp
  finally show "suma_impares (Suc n) = (Suc n) * (Suc n)" by simp
qed

 Demostración del lema anterior con patrones y razonamiento 
   ecuacional
lemma "suma_impares n = n * n" (is "?P n")
proof (induct n)
  show "?P 0" by simp
next
  fix n 
  assume HI: "?P n"
  have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n" 
    by simp
  also have "… = (2 * (Suc n) - 1) + n * n" using HI by simp
  also have "… = n * n + 2 * n + 1" by simp
  finally show "?P (Suc n)" by simp
qed

text {*
  Comentario sobre la demostración anterior:
  · Con la expresión
       "suma_impares n = n * n" (is "?P n")
    se abrevia "suma_impares n = n * n" como "?P n". Por tanto, 
       "?P 0"       es una abreviatura de "suma_impares 0 = 0 * 0"
       "?P (Suc n)" es una abreviatura de "suma_impares (Suc n) = (Suc n) * (Suc n)"
  · En general, cualquier fórmula seguida de (is patrón) equipara el
    patrón con la fórmula. 
*}

 La demostración usando patrones es
lemma "suma_impares n = n * n" (is "?P n")
proof (induct n)
  show "?P 0" by simp
next
  fix n 
  assume "?P n"
  then show "?P (Suc n)" by simp
qed
  
 La demostración automática es
lemma "suma_impares n = n * n"
by (induct n) auto

text {* 
  Ejemplo de definición con existenciales. 
  Un número natural n es par si existe un natural m tal que n=m+m.   
*}

definition par :: "nat ⇒ bool" where
  "par n ≡ ∃m. n=m+m"

text {* 
  Ejemplo de inducción y existenciales: 
  Demostrar que para todo número natural n, se verifica que n*(n+1) par. 
*}

 Demostración detallada por inducción
lemma 
  fixes n :: "nat"
  shows "par (n*(n+1))"
proof (induct n)
  show "par (0*(0+1))" by (simp add: par_def)
next
  fix n 
  assume "par (n*(n+1))"
  then have "∃m. n*(n+1) = m+m" by (simp add:par_def)
  then obtain m where m: "n*(n+1) = m+m" ..
  then have "(Suc n)*((Suc n)+1) = (m+n+1)+(m+n+1)" by auto
  then have "∃m. (Suc n)*((Suc n)+1) = m+m" ..
  then show "par ((Suc n)*((Suc n)+1))" by (simp add:par_def)
qed

text {*
  Comentarios sobre la demostración anterior:
  · (fixes n :: "nat") es una abreviatura de "sea n un número natural".
*}

text {*
  En Isabelle puede demostrarse de manera más simple un lema equivalente
  usando en lugar de la función "par" la función "even" definida en la
  teoría Parity por
     even x ⟷ x mod 2 = 0"
*}

lemma 
  fixes n :: "nat"
  shows "even (n*(n+1))"
by auto

text {*
  Comentarios sobre la demostración anterior:
  · Para poder usar la función "even" de la librería Parity es necesario
    importar dicha librería. Por ello, antes del inicio de la teoría
    aparece 
       imports Main Parity
*}

text {*
  Para completar la demostración basta demostrar la equivalencia de las
  funciones "par" y "even". 
*}

lemma 
  fixes n :: "nat"
  shows "par n = even n"
proof - 
  have "par n = (∃m. n = m+m)" by (simp add:par_def)
  then show "par n = even n" by presburger
qed

text {*
  Comentarios sobre la demostración anterior:
  · "by presburger" indica que se use como método de demostración el
    algoritmo de decisión de la aritmética de Presburger.
*}

section {* Inducción estructural *}

text {*
  Inducción estructural:
  · En Isabelle puede hacerse inducción estructural sobre cualquier tipo
    recursivo.
  · La inducción matemática es la inducción estructural sobre el tipo de
    los naturales.
  · El esquema de inducción estructural sobre listas es
    · list.induct: ⟦P []; ⋀x ys. P ys ⟹ P (x # ys)⟧ ⟹ P zs
  · Para demostrar una propiedad para todas las listas basta demostrar
    que la lista vacía tiene la propiedad y que al añadir un elemento a una
    lista que tiene la propiedad se obtiene una lista que también tiene la
    propiedad. 
  · En Isabelle el principio de inducción sobre listas está formalizado
    mediante el teorema list.induct que puede verse con 
       thm list.induct
*}

text {*
  Concatenación de listas:
  En la teoría List.thy está definida la concatenación de listas (que
  se representa por @) como sigue
     append_Nil:  "[]@ys     = ys"
     append_Cons: "(x#xs)@ys = x#(xs@ys)"
*}

text {*
  Lema. [Ejemplo de inducción sobre listas]
  Demostrar que la concatenación de listas es asociativa.
*}

 La demostración estructurada es
lemma conc_asociativa: "xs @ (ys @ zs) = (xs @ ys) @ zs"
proof (induct xs)
  show "[] @ (ys @ zs) = ([] @ ys) @ zs"
  proof -
    have "[] @ (ys @ zs) = ys @ zs" by simp
    also have "… = ([] @ ys) @ zs" by simp
    finally show ?thesis .
  qed
next
  fix x xs
  assume HI: "xs @ (ys @ zs) = (xs @ ys) @ zs"
  show "(x#xs) @ (ys @ zs) = ((x#xs) @ ys) @ zs"
  proof -
    have "(x#xs) @ (ys @ zs) = x#(xs @ (ys @ zs))" by simp
    also have "… = x#((xs @ ys) @ zs)" using HI by simp
    also have "… = (x#(xs @ ys)) @ zs" by simp
    also have "… = ((x#xs) @ ys) @ zs" by simp
    finally show ?thesis .
  qed
qed

 La demostración automática es 
lemma conc_asociativa_1: "xs @ (ys @ zs) = (xs @ ys) @ zs"
by (induct xs) auto

section {* Heurísticas para la inducción *}

text {*
  Definición. [Definición recursiva de inversa]
  (inversa xs) la inversa de la lista xs. Por ejemplo,
     inversa [a,b,c] = [c,b,a] 
*}

fun inversa :: "'a list ⇒ 'a list" where
  "inversa [] = []" 
| "inversa (x#xs) = (inversa xs) @ [x]"

value "inversa [a,b,c]"

text {* 
  Definición. [Definición de inversa con acumuladores]
  (inversaAc xs) es la inversa de la lista xs calculada con
  acumuladores. Por ejemplo,
     inversaAc [a,b,c]       = [c,b,a] 
     inversaAcAux [a,b,c] [] = [c,b,a] 
*}

fun inversaAcAux :: "'a list ⇒ 'a list ⇒ 'a list" where
  "inversaAcAux [] ys     = ys" 
| "inversaAcAux (x#xs) ys = inversaAcAux xs (x#ys)"

definition inversaAc :: "'a list ⇒ 'a list" where
  "inversaAc xs ≡ inversaAcAux xs []"

value "inversaAcAux [a,b,c] []"
value "inversaAc [a,b,c]"

text {* 
  Lema. [Ejemplo de equivalencia entre las definiciones]
  La inversa de [a,b,c] es lo mismo calculada con la primera definición
  que con la segunda.
*}

lemma "inversaAc [a,b,c] = inversa [a,b,c]"
by (simp add: inversaAc_def)

text {*
  Nota. [Ejemplo fallido de demostración por inducción]
  El siguiente intento de demostrar que para cualquier lista xs, se
  tiene que  "inversaAc xs = inversa xs" falla.
*}

lemma "inversaAc xs = inversa xs"
proof (induct xs)
  show "inversaAc [] = inversa []" by (simp add: inversaAc_def)
next
  fix a xs assume HI: "inversaAc xs = inversa xs"
  have "inversaAc (a#xs) = inversaAcAux (a#xs) []" 
    by (simp add: inversaAc_def)
  also have "… = inversaAcAux xs [a]" by simp
  also have "… = inversa (a#xs)"
  (* Problema: la hipótesis de inducción no es aplicable. *)
oops

text {* 
  Nota. [Heurística de generalización]
  Cuando se use demostración estructural, cuantificar universalmente las 
  variables libres (o, equivalentemente, considerar las variables libres
  como variables arbitrarias).

  Lema. [Lema con generalización]
  Para toda lista ys se tiene 
     inversaAcAux xs ys = (inversa xs) @ ys
*}

 La demostración estructurada es 
lemma inversaAcAux_es_inversa:
  "inversaAcAux xs ys = (inversa xs)@ys"
proof (induct xs arbitrary: ys)
  show "⋀ys. inversaAcAux [] ys = (inversa [])@ys" by simp
next
  fix a xs 
  assume HI: "⋀ys. inversaAcAux xs ys = inversa xs@ys"
  show "⋀ys. inversaAcAux (a#xs) ys = inversa (a#xs)@ys"
  proof -
    fix ys
    have "inversaAcAux (a#xs) ys = inversaAcAux xs (a#ys)" by simp
    also have "… = inversa xs@(a#ys)" using HI by simp
    also have "… = inversa (a#xs)@ys" using [[simp_trace]] by simp 
    finally show "inversaAcAux (a#xs) ys = inversa (a#xs)@ys" by simp
  qed
qed

 La demostración automática es
lemma inversaAcAux_es_inversa_1:
  "inversaAcAux xs ys = (inversa xs)@ys"
by (induct xs arbitrary: ys) auto

text {*
  Corolario.  Para cualquier lista xs, se tiene que
     inversaAc xs = inversa xs
*}

corollary "inversaAc xs = inversa xs"
  using [[simp_trace]] 
  by (simp add: inversaAcAux_es_inversa inversaAc_def)

text {*
  Nota. En el paso "inversa xs@(a#ys) = inversa (a#xs)@ys" se usan
  lemas de la teoría List. Se puede observar, insertano 
     using [[simp_trace]]
  entre la igualdad y by simp, que los lemas usados son 
  · List.append_simps_1: []@ys = ys
  · List.append_simps_2: (x#xs)@ys = x#(xs@ys)
  · List.append_assoc:   (xs @ ys) @ zs = xs @ (ys @ zs)
  Las dos primeras son las ecuaciones de la definición de append.

  En la siguiente demostración se detallan los lemas utilizados.
*}

lemma "(inversa xs)@(a#ys) = (inversa (a#xs))@ys"
proof -
  have "(inversa xs)@(a#ys) = (inversa xs)@(a#([]@ys))" 
    by (simp only: append.simps(1))
  also have "… = (inversa xs)@([a]@ys)" by (simp only: append.simps(2))
  also have "… = ((inversa xs)@[a])@ys" by (simp only: append_assoc)
  also have "… = (inversa (a#xs))@ys" by (simp only: inversa.simps(2))
  finally show ?thesis .
qed

section {* Recursión general. La función de Ackermann *}

text {* 
  El objetivo de esta sección es mostrar el uso de las definiciones
  recursivas generales y sus esquemas de inducción. Como ejemplo se usa la
  función de Ackermann (se puede consultar información sobre dicha función en
  http://en.wikipedia.org/wiki/Ackermann_function).

  Definición.  La función de Ackermann se define por
    A(m,n) = n+1,             si m=0,
             A(m-1,1),        si m>0 y n=0,
             A(m-1,A(m,n-1)), si m>0 y n>0
  para todo los números naturales. 

  La función de Ackermann es recursiva, pero no es primitiva recursiva. 
*}

fun ack :: "nat ⇒ nat ⇒ nat" where
  "ack 0       n       = n+1" 
| "ack (Suc m) 0       = ack m 1" 
| "ack (Suc m) (Suc n) = ack m (ack (Suc m) n)"

 Ejemplo de evaluación
value "ack 2 3" (* devuelve 9 *)

text {*
  Esquema de inducción correspondiente a una función:
  · Al definir una función recursiva general se genera una regla de
    inducción. En la definición anterior, la regla generada es
    ack.induct: 
       ⟦⋀n. P 0 n; 
        ⋀m. P m 1 ⟹ P (Suc m) 0;
        ⋀m n. ⟦P (Suc m) n; P m (ack (Suc m) n)⟧ ⟹ P (Suc m) (Suc n)⟧
       ⟹ P a b
*}

text {*
  Ejemplo de demostración por la inducción correspondiente a una función:
  Demostrar que para todos m y n, A(m,n) > n.
*} 

 La demostración detallada es
lemma "ack m n > n"
proof (induct m n rule: ack.induct)
  fix n
  show "ack 0 n > n" by simp
next
  fix m 
  assume "ack m 1 > 1"
  then show "ack (Suc m) 0 > 0" by simp
next  
  fix m n
  assume "n < ack (Suc m) n" and 
         "ack (Suc m) n < ack m (ack (Suc m) n)"
  then show "Suc n < ack (Suc m) (Suc n)" by simp
qed

text {*
  Comentarios sobre la demostración anterior:
  · (induct m n rule: ack.induct) indica que el método de demostración
    es el esquema de recursión correspondiente a la definición de 
    (ack m n).
  · Se generan 3 casos:
    1. ⋀n. n < ack 0 n
    2. ⋀m. 1 < ack m 1 ⟹ 0 < ack (Suc m) 0
    3. ⋀m n. ⟦n < ack (Suc m) n; 
              ack (Suc m) n < ack m (ack (Suc m) n)⟧
             ⟹ Suc n < ack (Suc m) (Suc n)
*}

 La demostración automática es
lemma "ack m n > n"
by (induct m n rule: ack.induct) auto

end