El algoritmo de Quine-McCluskey ( QMC ), también conocido como el método de implicantes primos , es un método utilizado para la minimización de funciones booleanas que fue desarrollado por Willard V. Quine en 1952 [1] [2] y ampliado por Edward J. McCluskey en 1956. [3] Como principio general, este enfoque ya había sido demostrado por el lógico Hugh McColl en 1878, [4] [5] [6] fue probado por Archie Blake en 1937, [7] [8] [9] [6] y fue redescubierto por Edward W. Samson y Burton E. Mills en 1954 [10] [6] y por Raymond J. Nelson en 1955. [11] [6] También en 1955, Paul W. Abrahams y John G. Nordahl [12] así como Albert A. Mullin y Wayne G. Kellner [13] [14] [15] [16] propusieron una variante decimal del método. [17] [14] [15] [16] [18] [19] [20] [21]
El algoritmo de Quine-McCluskey es funcionalmente idéntico al mapeo de Karnaugh , pero la forma tabular lo hace más eficiente para su uso en algoritmos informáticos y también proporciona una forma determinista de verificar que se ha alcanzado la forma mínima de un booleano F. A veces se lo denomina método de tabulación.
El algoritmo de Quine-McCluskey funciona de la siguiente manera:
Aunque es más práctico que el mapeo de Karnaugh cuando se trabaja con más de cuatro variables, el algoritmo de Quine-McCluskey también tiene un rango de uso limitado ya que el problema que resuelve es NP-completo . [22] [23] [24] El tiempo de ejecución del algoritmo de Quine-McCluskey crece exponencialmente con el número de variables. Para una función de n variables el número de implicantes primos puede ser tan grande como , [25] por ejemplo para 32 variables puede haber más de 534 × 10 12 implicantes primos. Las funciones con un gran número de variables deben minimizarse con métodos heurísticos potencialmente no óptimos , de los cuales el minimizador lógico heurístico Espresso fue el estándar de facto en 1995. [ necesita actualización ] [26] Para una clase natural de funciones , la complejidad precisa de encontrar todos los implicantes primos se entiende mejor: Milan Mossé, Harry Sha y Li-Yang Tan descubrieron un algoritmo casi óptimo para encontrar todos los implicantes primos de una fórmula en forma normal conjuntiva . [27]
El segundo paso del algoritmo consiste en resolver el problema de cobertura de conjuntos ; [28] Pueden ocurrir instancias NP-duras de este problema en este paso del algoritmo. [29] [30]
En este ejemplo, la entrada es una función booleana en cuatro variables, que evalúa como en los valores y , evalúa como un valor desconocido en y , y como en cualquier otro lugar (donde estos números enteros se interpretan en su forma binaria para la entrada como para la concisión de la notación). Las entradas que evalúan como se denominan "minterms". Codificamos toda esta información escribiendo
Esta expresión indica que la función de salida f será 1 para los mintérminos y (indicados por el término 'm') y que no nos importa la salida para las combinaciones y (indicadas por el término 'd'). El símbolo de suma indica la suma lógica (OR lógico o disyunción) de todos los términos que se suman.
Primero, escribimos la función como una tabla (donde 'x' significa no importa):
La expresión de suma canónica de productos se puede formar fácilmente a partir de esta tabla, simplemente sumando los mintérminos (omitiendo los términos que no importan ) donde la función evalúa uno:
que no es mínimo. Por lo tanto, para optimizar, todos los minterms que evalúan a uno se colocan primero en una tabla de minterms. Los términos que no importan también se agregan a esta tabla (nombres entre paréntesis), de modo que se puedan combinar con los minterms:
En este punto, se pueden empezar a combinar los mintérminos con otros mintérminos en grupos adyacentes. Si dos términos difieren en un solo dígito, ese dígito se puede reemplazar con un guión que indica que el dígito no importa. Los términos que ya no se pueden combinar se marcan con un asterisco ( * ). Por ejemplo, 1000
y 1001
se pueden combinar para dar 100-
, lo que indica que ambos mintérminos implican que el primer dígito es 1
y los dos siguientes son 0
.
Al pasar del tamaño 2 al tamaño 4, trátelo -
como un tercer valor de bit. Empareje -
primero los '. Los términos representan productos y para combinar dos términos de producto deben tener las mismas variables. Una de las variables debe complementarse en un término y no complementarse en el otro. Las variables restantes presentes deben coincidir. Entonces, para hacer coincidir dos términos, los -
' deben alinearse y todos menos uno de los otros dígitos deben ser iguales. Por ejemplo, -110
y -100
se pueden combinar para dar -1-0
, como pueden -110
y -010
para dar --10
, pero -110
y 011-
no pueden ya que los -
' no se alinean. -110
corresponde a BCD' mientras que 011-
corresponde a A'BC, y BCD' + A'BC no es equivalente a un término de producto.
Nota: En este ejemplo, ninguno de los términos de la tabla de implicantes de tamaño 4 se puede combinar más. En general, este proceso debe continuar (tamaños 8, 16, etc.) hasta que no se puedan combinar más términos.
Ninguno de los términos puede combinarse más allá de esto, por lo que en este punto construimos una tabla de implicantes primos esenciales. A lo largo del lateral van los implicantes primos que se acaban de generar (estos son los que se han marcado con un " * " en el paso anterior), y en la parte superior van los mintérminos especificados anteriormente. Los términos que no importan no se colocan en la parte superior; se omiten de esta sección porque no son entradas necesarias.
Para encontrar los implicantes primos esenciales, buscamos columnas con un solo "✓". Si una columna tiene un solo "✓", esto significa que el mintérmino solo puede ser cubierto por un implicante primo. Este implicante primo es esencial .
Por ejemplo: en la primera columna, con el mintérmino 4, solo hay un "✓". Esto significa que m(4,12) es esencial (por lo tanto, se marca con # ). El mintérmino 15 también tiene un solo "✓", por lo que m(10,11,14,15) también es esencial. Ahora todas las columnas con un "✓" están cubiertas. Las filas con los mintérminos m(4,12) y m(10,11,14,15) ahora se pueden eliminar, junto con todas las columnas que cubren.
El segundo implicante primo puede ser "cubierto" por el tercero y el cuarto, y el tercer implicante primo puede ser "cubierto" por el segundo y el primero, y por lo tanto ninguno es esencial. Si un implicante primo es esencial, entonces, como sería de esperar, es necesario incluirlo en la ecuación booleana minimizada. En algunos casos, los implicantes primos esenciales no cubren todos los mintérminos, en cuyo caso se pueden emplear procedimientos adicionales para la reducción de gráficos. El "procedimiento adicional" más simple es el de prueba y error, pero una forma más sistemática es el método de Petrick . En el ejemplo actual, los implicantes primos esenciales no manejan todos los mintérminos, por lo que, en este caso, los implicantes esenciales se pueden combinar con uno de los dos no esenciales para producir una ecuación:
o
Ambas ecuaciones finales son funcionalmente equivalentes a la ecuación original y detallada:
El pseudocódigo que se muestra a continuación calcula de forma recursiva los implicantes primos dada la lista de mintérminos de una función booleana. Para ello, intenta fusionar todos los mintérminos posibles y filtra los mintérminos que se han fusionado hasta que no se puedan realizar más fusiones de los mintérminos y, por lo tanto, se hayan encontrado los implicantes primos de la función.
// Calcula los implicantes primos de una lista de mintérminos.// cada mintérmino tiene la forma "1001", "1010", etc. y puede representarse con una cadena.La función getPrimeImplicants(lista minterms) es primeImplicants ← lista vacía fusiona ← nueva matriz booleana de longitud igual al número de minterms, cada uno establecido como falso número de fusiones ← 0 mergedMinterm, minterm1, minterm2 ← cadenas vacías para i = 0 a longitud(minterms) hacer para c = i + 1 a longitud(minterms) hacer minterm1 ← minterms[i] minterm2 ← mintérminos[c] // Comprobando que dos minterms se pueden fusionar si CheckDashesAlign(minterm1, minterm2) y CheckMintermDifference(minterm1, minterm2) entonces fusionadoMinterm ← MergeMinterms(minterm1, minterm2) Si primeImplicants no contiene mergedMinterm entonces primeImplicants.Add(mergedMinterm) númeroDeFusiones ← númeroDeFusiones + 1 fusiona[i] ← verdadero fusiona[c] ← verdadero // Filtrar todos los minitérminos que no se han fusionado porque son implicantes primos. También eliminar duplicados para j = 0 a length(minterms) hacer si merges[j] == false && primeImplicants no contiene minterms[j] entonces primeImplicants.Add(minterms[j]) // si no se han producido fusiones, entonces se han encontrado todos los implicantes principales, por lo que se retorna; de lo contrario //seguir fusionando los minterms. si numberOfmerges == 0 entonces devuelve primeImplicants de lo contrario devuelve getPrimeImplicants(primeImplicants)
En este ejemplo, las funciones CheckDashesAlign
y CheckMintermDifference
realizan las comprobaciones necesarias para determinar si se pueden fusionar dos mintérminos. La función MergeMinterms
fusiona los mintérminos y agrega los guiones donde sea necesario. Las funciones de utilidad que se muestran a continuación suponen que cada mintérmino se representará mediante cadenas.
La función MergeMinterms(minterm1, minterm2) es mergedMinterm ← cadena vacía para i = 0 a length(minterm1) hacer //Si los bits varían, reemplácelos con un guion; de lo contrario, el bit permanece en el minterm fusionado. si minterm[i] != minterm2[i] entonces términoMinterm fusionado ← términoMinterm fusionado + '-' demás términoMinterm fusionado ← términoMinterm fusionado + minterm1[i] Devuelve el término fusionadoLa función CheckDashesAlign(minterm1, minterm2) es para i = 0 a length(minterm1 ) // Si un minterm tiene guiones y el otro no, entonces los minterms no se pueden fusionar. si minterm1[i] != '-' && minterm2[i] == '-' entonces devuelve falso devuelve verdaderoLa función CheckMintermDifference(minterm1, minterm2) es // minterm1 y minterm2 son cadenas que representan todos los implicantes primos encontrados actualmente y fusionados // minitérminos. Algunos ejemplos son '01--' y '10-0' m1, m2 ← representación entera de minterm1 y minterm2 con los guiones eliminados, estos se reemplazan con 0 // ^ aquí hay un XOR bit a bit res ← m1 ^ m2 devuelve res != 0 && (res & res - 1) == 0
El pseudocódigo a continuación se puede dividir en dos secciones:
El diagrama de implicantes primos se puede representar mediante un diccionario donde cada clave es un implicante primo y el valor correspondiente es una cadena vacía que almacenará una cadena binaria una vez que se complete este paso. Cada bit de la cadena binaria se utiliza para representar los ticks dentro del diagrama de implicantes primos. El diagrama de implicantes primos se puede crear siguiendo los pasos siguientes:
\d
código del carácter. Esto crea una expresión regular que se puede comparar con cada uno de los minitérminos en busca de coincidencias."1"
a la cadena correspondiente en el diccionario. De lo contrario, agregar un "0"
.Cuando se escribe en pseudocódigo, el algoritmo descrito anteriormente es:
función CreatePrimeImplicantChart(lista primeImplicants, lista minterms) primeImplicantChart ← nuevo diccionario con clave de tipo cadena y valor de tipo cadena // Creando el gráfico vacío con los implicantes primos como clave y cadenas vacías como valor. para i = 0 a length(primeImplicants) hacer //Añadiendo un nuevo implicante primo al gráfico. primeImplicantChart.Add(primeImplicants[i], "") para i = 0 a length(primeImplicantChart.Keys) hacer primeImplicant ← primeImplicantChart.Keys[i] // Convierte "-" en "\d", que puede usarse para encontrar la fila de marcas de arriba. expresiónRegular ← Convertir a expresiónRegular(implicante principal) para j = 0 a length(minterms) hacer // Si hay una coincidencia entre la expresión regular y el minterm, agregue un 1, de lo contrario, 0. Si regularExpression.matches(minterms[j]) entonces primeImplicantChart[primeImplicant] += "1" demás primeImplicantChart[primeImplicant] += "0" // La tabla de implicantes primos está completa, por lo tanto, devuelve la tabla completa. Devolver primeImplicantChart
La función de utilidad, ConvertToRegularExpression
, se utiliza para convertir el implicante primo en la expresión regular para verificar coincidencias entre el implicante y los mintérminos.
función ConvertToRegularExpression(cadena primeImplicant) regularExpression ← nueva cadena para i = 0 a length(primeImplicant) hacer si primeImplicant[i] == "-" entonces //Añade el carácter literal "\d". expresión regular += @"\d" demás expresión regular += primeImplicant[i] devolver expresión regular
Utilizando la función, CreatePrimeImplicantChart
, definida arriba, podemos encontrar los implicantes primos esenciales simplemente iterando columna por columna de los valores en el diccionario, y donde "1"
se encuentra uno solo, entonces se ha encontrado un implicante primo esencial.
[...] Si la reducción [de una expresión a la forma más simple] no es evidente, se puede facilitar tomando la expresión negativa, reduciéndola y luego restaurándola a la forma positiva. [...]
[...] este método era conocido por
Peirce
y sus estudiantes [...] Se menciona en varios lugares en Studies in Logic, por miembros de la
Universidad Johns Hopkins
, 1883 [...](ii+60 páginas)
[...] Es un placer registrar que este tratamiento se basa en el trabajo de dos estudiantes durante el período en que estudiaban Circuitos de Conmutación en el Instituto Tecnológico de Massachusetts. Discutieron el método de forma independiente y luego colaboraron en la preparación de un memorando de clase: PW Abraham y JG Nordahl [...](xviii+686 páginas) (NB: El primer tratado importante sobre el método decimal que aparece en este libro se conoce a veces, de manera engañosa, como "la tabulación decimal de Caldwell").
[...] Los resultados de este artículo se presentan en el libro más disponible de SH Caldwell [...]. En este libro, el autor da crédito a Mullin y Kellner por el desarrollo de las manipulaciones con los números decimales.(1 página)
{{cite book}}
: Mantenimiento de CS1: falta la ubicación del editor ( enlace )(4 páginas) (NB: Algunas fuentes mencionan a los autores como "PW Abraham" e "IG Nordahl", el título también se cita como "Procedimiento de reducción McCluskey-Quine modificado").[...] 1955 wurde das Verfahren auf die bequemere dezimale Schreibweise umgestellt (PW Abraham und IG Nordahl en [Caldwell]). [...](NB. También existe una segunda edición de 1973.)
{{cite book}}
: Mantenimiento de CS1: errores de ISBN ignorados ( enlace )(519 páginas) [5]