Nivel 8 · Quicksort verificado (experto)
Qué significa «correcto»
El módulo anterior aplicó el marco de Lean a una lógica: el teorema de completitud de la
página de completitudquedó certificado por el núcleo. Este módulo lo aplica a un algoritmo: el mismo principio de «pruebas como términos» de la correspondencia de Curry–Howard y la misma auditoría de axiomas certifican ahora la corrección de un programa. Es el segundo estudio de caso de la teoría de los módulos anteriores, y antes de probar nada hay que fijar, con precisión total, qué se va a probar.
Dos mitades independientes
Un algoritmo de ordenamiento toma una secuencia finita de elementos de un dominio totalmente ordenado y los reorganiza en orden no decreciente. «Reorganiza» es la palabra clave: un algoritmo de ordenamiento no puede inventar, descartar ni duplicar elementos. Por ello la corrección tiene dos partes independientes, ambas necesarias:
- Ordenamiento. La salida está en orden no decreciente.
- Permutación. La salida contiene exactamente los mismos elementos que la entrada, con las mismas multiplicidades (es una reorganización).
Sea un tipo linealmente ordenado. Una función ordena si para toda lista ,
donde denota «es una permutación de» y «está en orden no decreciente». El objetivo del módulo es probar que quicksort satisface ambos conyuntos.
Este es el punto metodológico central de la especificación, y la tesis lo ilustra con los dos contraejemplos mínimos. La función constante que devuelve produce siempre una lista ordenada, pero no es una permutación de la entrada: satisface la mitad de ordenamiento y viola la de permutación. La función identidad devuelve siempre una permutación de la entrada (la trivial), pero no la ordena: satisface la mitad de permutación y viola la de ordenamiento. Solo la conjunción de las dos mitades caracteriza «ordenar». Una especificación incompleta es una especificación falsa: un teorema que probara únicamente sería verificado por el núcleo con la misma solemnidad para la función constante .
«Ordenada», con precisión
Se toma la definición de teoría del orden más cómoda para razonar por inducción: una lista está ordenada cuando cada elemento precede (o iguala) a todo elemento posterior.
Una lista está ordenada si siempre que . De forma equivalente, escribiendo el predicado como una relación por pares,
es decir, para cualquier elección de un elemento anterior y uno posterior en , se tiene .
La formulación por pares es conveniente porque se reparte limpiamente sobre la concatenación, que
es exactamente la operación con la que quicksort ensambla su salida (véase el lema de la
página de ordenamiento).
En Lean la definición es un traslado literal, en
Thesis/Sort/Quicksort.lean:
def Sorted (l : List α) : Prop := l.Pairwise (· ≤ ·)«Permutación», con precisión
Dos listas son permutaciones una de otra, escrito , si una se obtiene de la otra reordenando. Formalmente, es la menor relación de equivalencia sobre listas cerrada bajo el intercambio de elementos adyacentes; equivalentemente, si y solo si cada valor aparece el mismo número de veces en que en (igualdad como multiconjuntos). La permutación es reflexiva, simétrica y transitiva, es una congruencia para la concatenación ( y implican ) e implica igualdad de pertenencia ().
Estas propiedades de clausura estándar de son los únicos hechos sobre permutaciones que se
usarán; en Lean son Perm.append, Perm.cons, Perm.trans, perm_middle y Perm.mem_iff. La
condición de multiplicidad importa: debe ordenarse a , no a ; un
ordenamiento no es una deduplicación.
Lo que se verifica por máquina en este módulo es la corrección funcional de quicksort
(permutación y ordenamiento) sobre listas de un tipo linealmente ordenado cualquiera, en su
versión funcional (basada en filter, no en una variante in-place sobre arreglos). Quedan
fuera del alcance el análisis de complejidad (los casos promedio y peor
), el uso de memoria y las estrategias de elección de pivote: aquí interesa qué calcula
el algoritmo, no cuán rápido.
El plan del módulo
Con la especificación fijada, el desarrollo tiene cuatro etapas, cada una con su matemática y su
realización en Lean lado a lado: la definición del algoritmo y su terminación (que no es
gratuita, porque la recursión no es estructural), la mitad de permutación, la mitad de
ordenamiento, y la combinación final con su auditoría de axiomas. Como en el módulo anterior,
el desarrollo no usa ningún sorry ni lema postulado; los archivos fuente son
Thesis/Sort/Quicksort.lean,
Thesis/Sort/Examples.lean
y
Thesis/Sort/Audit.lean.
El marco teórico que hace posible todo esto es el de
(Avigad et al., 2015)
sobre la
base del CIC de
(Coquand y Huet, 1988)
y
(Coquand y Paulin, 1990)
.