01 · Punto de partida
Adyacencia dibujada
El método de Quine-McCluskey funciona, pero para encontrar vecinos hay que comparar números binarios uno por uno. A un humano le sale mucho mejor ver que calcular. Maurice Karnaugh propuso en 1953 colocar las celdas de la tabla de verdad en una cuadrícula con un orden especial, de modo que dos celdas pegadas siempre difieran en exactamente un bit. Desde ese momento, buscar vecinos es mirar.
Una escena típica. Tenés una función de cuatro variables con ocho unos desparramados por la tabla de verdad. En el papel, las filas están ordenadas por número y los vecinos quedan a saltos: la fila 1 y la 9 se parecen, pero están a ocho renglones de distancia. En el mapa, esos unos forman manchas compactas. Encerrás cada mancha, leés qué variables no cambian dentro de ella, y tenés la expresión mínima.
Es el mismo algoritmo del Tema 15, pero con el detector de adyacencias en la corteza visual de tu cerebro. Para tres o cuatro variables, nada lo supera en velocidad.
- ¿Por qué el orden de las filas y columnas no es el binario normal?
- ¿Qué rectángulos valen, y por qué los bordes del mapa se tocan?
- ¿Cómo se lee una expresión de un grupo?
- ¿Cómo se usan las condiciones de «no importa»?
- ¿Hasta cuántas variables sirve el mapa?
02 · El mapa
Orden Gray: vecinos de verdad
Si ordenaras las columnas en binario normal (00, 01, 10, 11), la columna 01 quedaría junto a la 10, pero esas dos difieren en dos bits: serían «vecinas falsas». El orden de Gray, ya visto en el Tema 5, evita el problema: 00 → 01 → 11 → 10. Entre dos consecutivas cambia siempre un solo bit. Y la última (10) también difiere en un bit de la primera (00), así que el mapa «se enrolla»: sus bordes opuestos se tocan.
Los mapas de 2, 3 y 4 variables
Cada celda es un minterm. El número de la celda es el valor binario de sus variables (A el bit más pesado):
| A \ B | 0 | 1 |
|---|---|---|
| 0 | m0 | m1 |
| 1 | m2 | m3 |
| A \ BC | 00 | 01 | 11 | 10 |
|---|---|---|---|---|
| 0 | m0 | m1 | m3 | m2 |
| 1 | m4 | m5 | m7 | m6 |
| AB \ CD | 00 | 01 | 11 | 10 |
|---|---|---|---|---|
| 00 | m0 | m1 | m3 | m2 |
| 01 | m4 | m5 | m7 | m6 |
| 11 | m12 | m13 | m15 | m14 |
| 10 | m8 | m9 | m11 | m10 |
- Los números parecen desordenados
- Es lo esperado: los minterms no siguen el orden natural, siguen el orden Gray en cada eje. No hace falta memorizar la tabla: se reconstruye anotando las etiquetas de filas y columnas y concatenándolas (fila 11, columna 01 → 1101 = m13).
- Cada celda tiene n vecinos
- En un mapa de n variables, cada celda tiene exactamente n vecinas (las que difieren en un bit): arriba, abajo, a izquierda y derecha, contando los bordes pegados. En el mapa de 4 variables son cuatro. En el de 2 y 3 variables, la fila de arriba y la de abajo son vecinas entre sí.
- Es un toroide
- Si pegás el borde izquierdo con el derecho obtenés un cilindro; si además pegás el de arriba con el de abajo, un toroide (una dona). Un grupo puede «salir» por un borde y «entrar» por el opuesto, como en el Pac-Man.
03 · Reglas de agrupación
Cómo encerrar los unos
Una vez cargado el mapa (un 1 en cada minterm de la función, un 0 en el resto, una X en las condiciones de no importa), se encierran los unos en grupos que cumplan estas reglas:
- 1Rectángulos de 1, 2, 4, 8 o 16 celdas.
Solo potencias de dos. Tres celdas no forman un grupo: se cubren con dos grupos de dos que comparten una.
- 2Nada de diagonales.
Dos celdas en diagonal difieren en dos bits: no son vecinas. Un grupo es siempre un rectángulo (con los bordes pegados, también cuenta).
- 3Solo unos (o X), nunca ceros.
Un cero dentro de un grupo lo invalida.
- 4Lo más grandes posible.
Un grupo grande elimina más variables. Agrandá hasta que no puedas más: ese es un implicante primo.
- 5Cubrir todos los unos con la menor cantidad de grupos.
Primero los unos aislados y los que solo tienen una manera de agruparse (esenciales). Después, lo que falte, con los grupos más grandes posibles. Los grupos se pueden superponer.
Cómo se lee un grupo
Dentro de un grupo, algunas variables cambian y otras no. Las que cambian desaparecen; las que no cambian quedan: si valen 1, directas, y si valen 0, negadas. Un grupo de 2k celdas elimina k variables.
| Grupo | Celdas | Variables que quedan | Ejemplo |
|---|---|---|---|
| Una celda | 1 | 4 (minterm completo) | m6 = ~A·B·C·~D |
| Un par (horizontal o vertical) | 2 | 3 | m6, m7 = ~A·B·C |
| Una fila entera o una columna entera | 4 | 2 | fila 01 (m4 a m7) = ~A·B; columna 11 (m3, m7, m15, m11) = C·D |
| Un bloque de 2×2 | 4 | 2 | m5, m7, m13, m15 = B·D |
| Las cuatro esquinas (por los bordes pegados) | 4 | 2 | m0, m2, m8, m10 = ~B·~D |
| Dos filas o dos columnas | 8 | 1 | filas 11 y 10 = A |
| Todo el mapa | 16 | 0 | función = 1 |
04 · Ejemplos
Tres ejemplos resueltos
Ejemplo 1: la mayoría de tres, Σm(3, 5, 6, 7)
| A \ BC | 00 | 01 | 11 | 10 |
|---|---|---|---|---|
| 0 | 0 | 0 | 1 (a) | 0 |
| 1 | 0 | 1 (b) | 1 (a, b, c) | 1 (c) |
Tres pares, todos esenciales (m3, m5 y m6 solo tienen una manera de agruparse). El m7 está en los tres: reuso. f = B·C + A·C + A·B, el mismo resultado del Tema 15, ahora en tres miradas.
Ejemplo 2: cuatro variables, Σm(0, 1, 3, 7, 8, 9, 11, 15)
| AB \ CD | 00 | 01 | 11 | 10 |
|---|---|---|---|---|
| 00 | 1 (a) | 1 (a, c) | 1 (b, c) | 0 |
| 01 | 0 | 0 | 1 (b) | 0 |
| 11 | 0 | 0 | 1 (b) | 0 |
| 10 | 1 (a) | 1 (a, c) | 1 (b, c) | 0 |
El grupo a cruza el borde (las filas 00 y 10 son vecinas). El b es una columna entera. El tercer grupo posible, c = ~B·D (m1, m3, m9, m11), es un implicante primo, pero no hace falta: todos sus unos ya están cubiertos por a y b. f = ~B·~C + C·D, con 4 literales, y se ve a simple vista lo que en el Tema 15 salió de una tabla de cobertura.
Ejemplo 3: las cuatro esquinas, Σm(0, 2, 8, 10)
| AB \ CD | 00 | 01 | 11 | 10 |
|---|---|---|---|---|
| 00 | 1 | 0 | 0 | 1 |
| 01 | 0 | 0 | 0 | 0 |
| 11 | 0 | 0 | 0 | 0 |
| 10 | 1 | 0 | 0 | 1 |
Las cuatro celdas son vecinas entre sí a través de los bordes. Dentro del grupo, B y D valen siempre 0 y A y C cambian: f = ~B·~D. Una sola puerta NOR (Tema 9). Sin el mapa, difícilmente verías que esas cuatro filas de la tabla, separadas, tienen algo en común.
05 · No importa y ceros
Las X y el producto de sumas
Las condiciones de «no importa»
Las X (Tema 6) se marcan en el mapa y funcionan como comodines: se incluyen en un grupo si lo agrandan, y se ignoran si no ayudan. Nunca es obligatorio cubrirlas, y no se pueden formar grupos solo de X.
| AB \ CD | 00 | 01 | 11 | 10 |
|---|---|---|---|---|
| 00 | 0 | 0 | 0 | 0 |
| 01 | 0 | 1 (b) | 1 (b, c) | 1 (c) |
| 11 | X (a) | X (a, b) | X (a, b, c) | X (a, c) |
| 10 | 1 (a) | 1 (a) | X (a) | X (a) |
Las X convirtieron grupos chicos en uno de ocho (A) y dos bloques de cuatro (B·D y B·C). f = A + B·C + B·D, con 5 literales; sin usar las X harían falta 9. Es el mismo ahorro del Tema 15, pero ahora lo ves.
Agrupar los ceros: producto de sumas
El mismo mapa sirve para obtener un producto de sumas: se agrupan los ceros (con las mismas reglas) y se obtiene la expresión de la función negada. Aplicando De Morgan (Tema 14) a cada grupo se pasa a la función directa: la variable que valía 0 en el grupo aparece directa y la que valía 1, negada, dentro de una suma.
| Grupo de ceros | Celdas | Término de ~f | Suma en f (De Morgan) |
|---|---|---|---|
| Par horizontal | m0, m1 | ~A·~B | (A + B) |
| Par por el borde | m0, m2 | ~A·~C | (A + C) |
| Par vertical | m0, m4 | ~B·~C | (B + C) |
f = (A + B)·(A + C)·(B + C). Mismo costo que la suma de productos, porque la mayoría es autodual. Para una función con pocos ceros y muchos unos, el producto de sumas suele ser mucho más corto: conviene probar los dos caminos.
06 · Límites
Hasta dónde llega el mapa
2 a 4 variables
El ojo humano
Cada celda tiene n vecinas visibles. El mapa se dibuja en un papel y los grupos se ven. La inmensa mayoría de los problemas de mesa viven acá.
5 o más variables
Software
Quine-McCluskey o Espresso en la computadora. El método es el mismo, pero el dibujo deja de ser fiel.
- Por qué falla a partir de 5
- Con 5 variables cada celda tiene 5 vecinos lógicos, pero en una cuadrícula una celda tiene solo 4 vecinos físicos. Siempre falta uno. Se dibujan dos mapas de 4 variables (E = 0 y E = 1) apilados, y las celdas en la misma posición de los dos son vecinas. Con 6 variables, cuatro mapas. Se puede, pero el ojo ya no ve con claridad.
- Cuando conviene partir
- En lugar de un mapa de 6 variables, es más práctico dividir el circuito en bloques (multiplexores, decodificadores, comparadores: Temas 18 a 25) y minimizar cada uno con un mapa de 3 o 4 variables.
- Varias salidas
- Un circuito con varias salidas (por ejemplo, un decodificador para un display de 7 segmentos) tiene un mapa por salida. Los grupos que se repiten entre mapas se pueden compartir, ahorrando puertas: es parte del diseño del Tema 17.
- Un detalle para más adelante
- El mapa también permite ver riesgos de glitches (Tema 28): cuando dos grupos adyacentes no se superponen, al pasar de uno al otro la salida puede titilar. Agregar un grupo redundante «de consenso» que cubra el empalme lo evita; el mínimo no siempre es el más seguro.
07 · Simulador
Laboratorio: el mapa que agrupa solo
Tocá las celdas del mapa para cambiar su valor (0 → 1 → X → 0). El simulador encuentra los implicantes primos con el método del Tema 15, elige la cobertura mínima y dibuja los grupos sobre el mapa, con un color distinto por grupo. Los grupos que cruzan los bordes se muestran con los extremos abiertos. A la derecha lee cada grupo y su término. Con el selector podés agrupar los unos (suma de productos) o los ceros (producto de sumas).
- Celdas: el número grande es el valor (1 amarillo, X naranja, 0 gris) y el número chico es el minterm.
- Contornos de colores: un grupo cada uno. Una celda puede estar en varios (reuso). Una «E» marca un grupo esencial.
- Teclado: cada celda se enfoca con Tab y se cambia con Enter o espacio.
Mapa de Karnaugh
Σm(3, 5, 6, 7) → B·C + A·C + A·B
Mayoría de 3 agrupada.
Recorrido guiado (8 minutos)
- 1Mirá la mayoría de 3 (estado inicial).
Tres pares: B·C en la columna 11, A·C abajo en las columnas 01 y 11, A·B abajo en las columnas 11 y 10. El m7 está dentro de los tres contornos.
- 2Apretá «Cuatro esquinas».
Las cuatro celdas están en los extremos del mapa pero forman un solo grupo, dibujado como cuatro esquinas con los bordes abiertos. Resultado: ~B·~D. Es lo que se pierde si olvidás que el mapa «se enrolla».
- 3Apretá «Con un primo redundante».
Dos grupos: ~B·~C (cruza el borde de arriba y abajo) y C·D (la columna 11 entera). Fijate en las celdas m1, m3, m9 y m11: forman un tercer grupo posible (~B·D) que el simulador no dibuja, porque todos sus unos ya están cubiertos por los otros dos.
- 4Apretá «BCD mayor que 4 (con X)».
Las X naranjas permiten el grupo de ocho (A) y los bloques BD y BC. Ahora cambiá las seis X a 0 con un clic cada una (X → 0) y mirá cómo se rompen los grupos y sube el costo.
- 5Cambiá a «Agrupar ceros» con la mayoría.
El mapa agrupa los ceros y la expresión pasa a producto de sumas: (A + B)(A + C)(B + C). Es la forma que da De Morgan sobre el mapa de los ceros.
- 6Apretá «Tablero (paridad de 4)».
Los unos forman un tablero de ajedrez: ninguno tiene vecino. Ocho grupos de una celda y costo máximo. Es la señal de que hay que usar XOR (Tema 10) en lugar de AND-OR.
- 7Con 4 variables, armá una función con una fila entera.
Poné todo en 0 y encendé una fila de cuatro celdas, por ejemplo la segunda: queda un solo grupo de cuatro con dos literales (~A·B).
- 8Armá un patrón propio y verificá.
Si el cuadro de estado dice «Verificado», la expresión coincide con tu mapa en todas las celdas (las X quedan libres). Probá encender unos al azar y buscá los grupos con los ojos antes de ver la respuesta.
Preguntas para pensar
- Dejá solo m0 y m3 en el mapa de 2 variables. ¿Cuántos grupos hay y por qué?
- Prendé todo el mapa. ¿Cuál es la expresión y cuántas puertas hacen falta?
- En las cuatro esquinas, ¿por qué se van A y C y quedan B y D?
- ¿Qué pasa con una función de todos ceros al agrupar los unos? ¿Y al agrupar los ceros?
Ver respuestas sugeridas
- Dos grupos de una celda: m0 y m3 están en diagonal, no son vecinas. La expresión es ~A·~B + A·B (XNOR). No hay nada que fusionar.
- Un solo grupo de todas las celdas: la función es la constante 1. Sin puertas: un cable a VCC.
- En las cuatro esquinas, A cambia (filas 00 y 10) y C cambia (columnas 00 y 10), así que desaparecen. B vale 0 en todas y D vale 0 en todas: quedan ~B y ~D.
- Al agrupar unos: no hay grupos y la expresión es 0. Al agrupar ceros: un grupo con todas las celdas (si ninguna es 1) y la función producto de sumas es la constante 0.
08 · Mapas en código
Construir, dibujar y validar mapas
Los bloques construyen el mapa en código Gray, comprueban que cada celda tiene exactamente n vecinas a un bit (incluyendo los bordes pegados), dibujan el mapa en ASCII, validan si un conjunto de celdas es un grupo legal, marcan los grupos de una función minimizada, comparan suma de productos con producto de sumas y muestran por qué el mapa deja de ser fiel con 5 o más variables.
Python en tu navegador. Los bloques son editables: cambiá los minterms y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
Paso 1: el mapa en Gray y sus vecinos
def gray(k):
return [i ^ (i >> 1) for i in range(2 ** k)]
print("Gray de 2 bits:", [format(g, "02b") for g in gray(2)])
print("Gray de 3 bits:", [format(g, "03b") for g in gray(3)])
FILAS_BITS = {2: 1, 3: 1, 4: 2}
def mapa(n):
fb = FILAS_BITS[n]
cb = n - fb
rv, cv = gray(fb), gray(cb)
return [[(r << cb) | c for c in cv] for r in rv]
def vecinos_en_mapa(grid):
R, C = len(grid), len(grid[0])
res = {}
for i in range(R):
for j in range(C):
vs = {grid[(i - 1) % R][j], grid[(i + 1) % R][j],
grid[i][(j - 1) % C], grid[i][(j + 1) % C]}
vs.discard(grid[i][j])
res[grid[i][j]] = vs
return res
for n in (2, 3, 4):
grid = mapa(n)
vec = vecinos_en_mapa(grid)
ok = all(len(v) == n and all(bin(m ^ x).count("1") == 1 for x in v) for m, v in vec.items())
print(f"{n} variables: mapa {len(grid)}x{len(grid[0])}, cada celda tiene {n} vecinas y todas difieren en 1 bit: {ok}")
# con orden binario normal aparecen vecinas falsas
binario = [[(r << 2) | c for c in range(4)] for r in range(4)]
falsas = sum(1 for m, v in vecinos_en_mapa(binario).items() for x in v if bin(m ^ x).count("1") != 1)
print("vecindades falsas en un mapa de 4 variables con orden binario:", falsas)
En Gray, todas las vecindades visibles son reales (n vecinas por celda, a un bit). Con orden binario normal aparecen decenas de vecindades falsas que llevarían a agrupar mal.
Paso 2: dibujar el mapa en ASCII
def gray(k):
return [i ^ (i >> 1) for i in range(2 ** k)]
FILAS_BITS = {2: 1, 3: 1, 4: 2}
def mostrar(unos, dcs, n):
fb = FILAS_BITS[n]
cb = n - fb
rv, cv = gray(fb), gray(cb)
vf, vc = "ABCD"[:fb], "ABCD"[fb:n]
print(f"{vf}\\{vc} " + " ".join(format(c, f"0{cb}b") for c in cv))
for r in rv:
celdas = []
for c in cv:
m = (r << cb) | c
celdas.append(("X" if m in dcs else "1" if m in unos else "0").center(cb))
etiqueta = format(r, f"0{fb}b").ljust(len(vf) + len(vc) + 1)
print(etiqueta + " " + " ".join(celdas))
print()
print("mayoría de 3:")
mostrar({3, 5, 6, 7}, set(), 3)
print("con un primo redundante:")
mostrar({0, 1, 3, 7, 8, 9, 11, 15}, set(), 4)
print("cuatro esquinas:")
mostrar({0, 2, 8, 10}, set(), 4)
print("BCD mayor que 4 (X en 10 a 15):")
mostrar({5, 6, 7, 8, 9}, set(range(10, 16)), 4)
Los cuatro mapas coinciden con los de la sección 04: en las esquinas los cuatro unos están separados en el dibujo pero son vecinos; en el BCD, las X de las filas 11 y 10 son las que arman el grupo A.
Paso 3: ¿es un grupo legal?
def es_grupo(celdas):
s = sorted(set(celdas))
k = len(s).bit_length() - 1
if len(s) != 2 ** k:
return False, "el tamaño no es potencia de 2"
var = 0
for m in s:
var |= m ^ s[0] # bits que cambian dentro del grupo
if bin(var).count("1") != k:
return False, "no forma un rectángulo (subcubo)"
return True, var
def patron(celdas, n):
s = list(celdas)
var = 0
for m in s:
var |= m ^ s[0]
return "".join("-" if (var >> (n - 1 - i)) & 1 else str((s[0] >> (n - 1 - i)) & 1) for i in range(n))
def termino(p, vs="ABCD"):
t = "·".join(v if x == "1" else "~" + v for v, x in zip(vs, p) if x != "-")
return t or "1"
PRUEBAS = [((1, 3), 2), ((0, 3), 2), ((0, 1, 2), 2), ((0, 2, 8, 10), 4),
((4, 5, 6, 7), 4), ((1, 2, 4, 7), 3), ((0, 1, 3, 7), 4), ((0, 1, 8, 9), 4)]
for celdas, n in PRUEBAS:
ok, info = es_grupo(celdas)
if ok:
print(f"{str(celdas):<14} grupo válido patrón {patron(celdas, n)} término {termino(patron(celdas, n))}")
else:
print(f"{str(celdas):<14} NO agrupa: {info}")
Un conjunto es un grupo legal si su tamaño es 2k y exactamente k bits cambian dentro de él. (0, 3) y (1, 2, 4, 7) no lo son (diagonales), (0, 1, 2) tiene tamaño 3 y (0, 1, 3, 7) cambia 3 bits para 4 celdas. Las esquinas (0, 2, 8, 10) sí: patrón -0-0, término ~B·~D.
Paso 4: minimizar y marcar los grupos en el mapa
from itertools import combinations
def gray(k):
return [i ^ (i >> 1) for i in range(2 ** k)]
FILAS_BITS = {2: 1, 3: 1, 4: 2}
def primos(unos, dcs, n):
actual = {format(m, f"0{n}b") for m in set(unos) | set(dcs)}
res = set()
while actual:
sig, usados = set(), set()
for a, b in combinations(sorted(actual), 2):
d = [i for i in range(n) if a[i] != b[i]]
if len(d) == 1 and "-" not in (a[d[0]], b[d[0]]):
i = d[0]
sig.add(a[:i] + "-" + a[i + 1:])
usados.update((a, b))
res |= actual - usados
actual = sig
return sorted(res)
def cubre(p, m, n):
b = format(m, f"0{n}b")
return all(x in ("-", y) for x, y in zip(p, b))
def termino(p, vs="ABCD"):
t = "·".join(v if x == "1" else "~" + v for v, x in zip(vs, p) if x != "-")
return t or "1"
def minima(unos, dcs, n):
ps = primos(unos, dcs, n)
esc = set()
for m in unos:
c = [p for p in ps if cubre(p, m, n)]
if len(c) == 1:
esc.add(c[0])
resto = [m for m in unos if not any(cubre(p, m, n) for p in esc)]
libres = [p for p in ps if p not in esc]
for k in range(len(libres) + 1):
sol = [c for c in combinations(libres, k)
if all(any(cubre(p, m, n) for p in c) for m in resto)]
if sol:
mejor = min(sol, key=lambda c: sum(x != "-" for p in c for x in p))
return sorted(esc | set(mejor)), ps
return sorted(esc), ps
def mapa_grupos(unos, dcs, n):
elegidos, ps = minima(unos, dcs, n)
letras = {p: chr(97 + i) for i, p in enumerate(elegidos)}
fb = FILAS_BITS[n]
cb = n - fb
rv, cv = gray(fb), gray(cb)
vf, vc = "ABCD"[:fb], "ABCD"[fb:n]
print(f"{vf}\\{vc} " + "".join(format(c, f"0{cb}b").ljust(6) for c in cv))
for r in rv:
fila = ""
for c in cv:
m = (r << cb) | c
en = "".join(letras[p] for p in elegidos if cubre(p, m, n))
fila += (en if en else ("." if m not in unos else "?")).ljust(6)
print(format(r, f"0{fb}b").ljust(len(vf) + len(vc) + 2) + fila)
for p in elegidos:
print(f" {letras[p]} = {p} {termino(p)}")
print(" primos descartados:", [termino(p) for p in ps if p not in elegidos] or "ninguno")
print()
print("primo redundante:")
mapa_grupos([0, 1, 3, 7, 8, 9, 11, 15], [], 4)
print("BCD > 4 con X:")
mapa_grupos([5, 6, 7, 8, 9], list(range(10, 16)), 4)
Cada celda muestra las letras de los grupos que la cubren (a, b, c…); «.» es un 0. En la primera función el primo ~B·D queda descartado (todos sus unos ya están cubiertos); en el BCD, las X de la derecha participan en los grupos A, B·D y B·C.
Paso 5: suma de productos frente a producto de sumas
from itertools import combinations
def primos(unos, dcs, n):
actual = {format(m, f"0{n}b") for m in set(unos) | set(dcs)}
res = set()
while actual:
sig, usados = set(), set()
for a, b in combinations(sorted(actual), 2):
d = [i for i in range(n) if a[i] != b[i]]
if len(d) == 1 and "-" not in (a[d[0]], b[d[0]]):
i = d[0]
sig.add(a[:i] + "-" + a[i + 1:])
usados.update((a, b))
res |= actual - usados
actual = sig
return sorted(res)
def cubre(p, m, n):
b = format(m, f"0{n}b")
return all(x in ("-", y) for x, y in zip(p, b))
def cobertura(ps, objetivo, n):
esc = set()
for m in objetivo:
c = [p for p in ps if cubre(p, m, n)]
if len(c) == 1:
esc.add(c[0])
resto = [m for m in objetivo if not any(cubre(p, m, n) for p in esc)]
libres = [p for p in ps if p not in esc]
for k in range(len(libres) + 1):
sol = [c for c in combinations(libres, k)
if all(any(cubre(p, m, n) for p in c) for m in resto)]
if sol:
return sorted(esc | set(min(sol, key=lambda c: sum(x != "-" for p in c for x in p))))
return sorted(esc)
VS = "ABCD"
def lits(p):
return sum(x != "-" for x in p)
def expr_sop(ps):
if not ps:
return "0"
return " + ".join("·".join((VS[i] if x == "1" else "~" + VS[i]) for i, x in enumerate(p) if x != "-") or "1" for p in ps)
def expr_pos(ps):
if not ps:
return "1"
if any(lits(p) == 0 for p in ps):
return "0"
return "·".join("(" + " + ".join((VS[i] if x == "0" else "~" + VS[i]) for i, x in enumerate(p) if x != "-") + ")" for p in ps)
def costo_sop(ps):
neg = {i for p in ps for i, x in enumerate(p) if x == "0"}
return sum(map(lits, ps)), sum(1 for p in ps if lits(p) >= 2) + (1 if len(ps) > 1 else 0) + len(neg)
def costo_pos(ps):
neg = {i for p in ps for i, x in enumerate(p) if x == "1"}
return sum(map(lits, ps)), sum(1 for p in ps if lits(p) >= 2) + (1 if len(ps) > 1 else 0) + len(neg)
FUNCIONES = {
"mayoría de 3": ([3, 5, 6, 7], [], 3),
"A + B·C": ([3, 4, 5, 6, 7], [], 3),
"XOR de 2": ([1, 2], [], 2),
"BCD mayor que 4": ([5, 6, 7, 8, 9], list(range(10, 16)), 4),
}
for nombre, (unos, dcs, n) in FUNCIONES.items():
ceros = [m for m in range(2 ** n) if m not in unos and m not in dcs]
s = cobertura(primos(unos, dcs, n), unos, n)
p = cobertura(primos(ceros, dcs, n), ceros, n)
print(f"{nombre}")
print(f" SOP: {expr_sop(s):<26} {costo_sop(s)} (literales, puertas)")
print(f" POS: {expr_pos(p):<26} {costo_pos(p)}")
La mayoría da lo mismo por los dos caminos (es autodual). En «A + B·C» la suma de productos es más corta que el producto de sumas; en otras funciones ocurre al revés. Con pocos ceros, conviene agrupar los ceros.
Paso 6: por qué el mapa se acaba en 4 variables
def gray(k):
return [i ^ (i >> 1) for i in range(2 ** k)]
def analizar(filas_bits, cols_bits):
rv, cv = gray(filas_bits), gray(cols_bits)
R, C = len(rv), len(cv)
grid = [[(r << cols_bits) | c for c in cv] for r in rv]
n = filas_bits + cols_bits
faltan = []
for i in range(R):
for j in range(C):
m = grid[i][j]
en_mapa = {grid[(i - 1) % R][j], grid[(i + 1) % R][j],
grid[i][(j - 1) % C], grid[i][(j + 1) % C]} - {m}
logicos = {m ^ (1 << b) for b in range(n)}
faltan.append(len(logicos - en_mapa))
return n, R, C, min(faltan), max(faltan)
for fb, cb in ((1, 1), (1, 2), (2, 2), (2, 3), (3, 3)):
n, R, C, lo, hi = analizar(fb, cb)
print(f"{n} variables: mapa {R}x{C} vecinas lógicas que NO son vecinas en el dibujo: {lo} a {hi} por celda")
Hasta 4 variables no falta ninguna. Con 5, a cada celda le falta una vecina lógica en el dibujo; con 6, dos. Por eso con 5 o más variables se usan mapas apilados o, mejor, un programa.
09 · Analogía de programador
Un heatmap con bordes pegados
| En software | En el mapa de Karnaugh |
|---|---|
| Un mapa de calor: se buscan las manchas | Los unos forman manchas rectangulares que se encierran |
| Una curva que preserva la vecindad (Hilbert, Z-order) para indexar una grilla | El orden Gray: lo que está cerca en el dibujo está cerca en bits |
| Un mundo toroidal (Pac-Man, el juego de la vida) | Los bordes pegados: salís por un lado y entrás por el opuesto |
| Componentes conexos de una imagen binaria | Los grupos de unos adyacentes |
Un comodín (.* en una expresión regular) | La X: se incluye si ayuda, se ignora si no |
Revisar el borde de un arreglo circular (i % n) | Revisar si un grupo cruza el borde del mapa |
| Una visualización que reduce el esfuerzo mental, con el mismo algoritmo atrás | El mapa: Quine-McCluskey hecho por los ojos |
| Un algoritmo que no escala (hay que pasar a herramientas) | El mapa a partir de 5 o 6 variables |
- 1Mapa = heatmap.
Los unos son calientes, los ceros fríos. Agrupar es encontrar las manchas.
- 2Gray = índice que conserva la vecindad.
Como las curvas de relleno de espacio: ordenar los datos para que lo cercano en el dibujo sea cercano en el dominio.
- 3Bordes pegados = módulo.
El mismo truco de
(i + 1) % nque usás en un buffer circular; el Paso 1 lo usa para verificar las vecinas.
10 · Comprensión
Confusiones frecuentes
«La diagonal agrupa»
Dos celdas en diagonal difieren en dos bits (m0 y m3 en el mapa de 2 variables). Encerrarían una XNOR, no un producto. Los grupos son rectángulos.
«Puedo agrupar tres celdas»
Los grupos son de 1, 2, 4, 8 o 16. Tres unos se cubren con dos pares que comparten una celda (reuso).
«Los bordes no se tocan»
Sí se tocan. Las columnas 00 y 10 son vecinas, y las filas 00 y 10 también. Olvidarlo es el error más caro: no se ven grupos como las cuatro esquinas.
«El orden de las etiquetas es el binario»
Es Gray: 00, 01, 11, 10. Con el orden binario normal aparecen vecinas falsas y se agrupa mal. Revisá siempre los encabezados.
«Un grupo más chico es igual de bueno»
Cada grupo debe ser lo más grande posible (un implicante primo). Dos pares donde cabía un cuatro dejan una variable de más en cada término.
«Las X siempre valen 1»
Valen lo que convenga: se incluyen en un grupo si lo agrandan, y se ignoran si no. Un grupo formado solo por X no tiene sentido.
«Al agrupar los ceros leo directamente la función»
Se lee la función negada (~f). Para obtener f hay que aplicar De Morgan a cada término: el producto ~A·~B pasa a la suma (A + B).
11 · Práctica guiada
Ejercicios de mapa
Ejercicio 1: Σm(0, 1, 2) en el mapa de 2 variables
Dibujá el mapa, agrupá (0, 1) y (0, 2) y escribí el mínimo. Verificalo en el simulador.
Ver solución razonada
(m0, m1) están en la fila A = 0: queda ~A. (m0, m2) están en la columna B = 0: queda ~B. F = ~A + ~B (una NAND). El m0 se reutiliza.
Ejercicio 2: los bordes pegados en 3 variables
Mapa de 3 variables con unos en m0, m2, m4 y m6. ¿Cuántos grupos hay y cuál es la expresión?
Ver solución razonada
m0 y m2 están en las columnas BC = 00 y 10, que son vecinas por el borde; lo mismo m4 y m6 en la fila de abajo. Las cuatro forman un grupo de cuatro (2×2 con borde): cambian A y B, queda ~C. F = ~C: un solo inversor.
Ejercicio 3: las cuatro esquinas
Para Σm(0, 2, 8, 10) en el mapa de 4 variables, encontrá el grupo y la expresión. ¿Qué puerta la implementa?
Ver solución razonada
Un grupo de cuatro formado por los extremos. Cambian A y C; quedan B = 0 y D = 0: F = ~B·~D. Es una NOR de B y D (Tema 9).
Ejercicio 4: dígito BCD válido
Un detector «el código es un dígito válido» (0 a 9) tiene las combinaciones 10 a 15 como X. Obtené la forma mínima con el mapa.
Ver solución razonada
Los unos son m0 a m9. Con las X en las filas 11 y 10 (menos m8 y m9, que son unos), el grupo de ocho de las filas 00 y 01 da ~A; los unos m8 y m9 se cubren con el bloque ~B·~C (m0, m1, m9, m8). Resultado: V = ~A + ~B·~C, que es lo mismo que ~(A·B + A·C): 0 solo cuando A = 1 y (B o C).
Ejercicio 5: el tablero
Una función de 4 variables vale 1 en m1, m2, m4, m7, m8, m11, m13 y m14 (la paridad impar). Dibujá el mapa. ¿Qué ves? ¿Qué convendría usar?
Ver solución razonada
Los unos forman un tablero de ajedrez: ninguno tiene un vecino con 1, así que no se puede fusionar nada. Ocho grupos de una celda: 32 literales. Lo conveniente es una cadena de XOR (3 puertas XOR), que es la estructura natural de la paridad (Tema 10).
Ejercicio 6: producto de sumas con el mapa
Para la mayoría de 3, agrupá los ceros (m0, m1, m2, m4) en el mapa de 3 variables y obtené la función como producto de sumas.
Ver solución razonada
Tres pares de ceros: (m0, m1) = ~A·~B; (m0, m2) = ~A·~C (por el borde); (m0, m4) = ~B·~C. Entonces ~f = ~A·~B + ~A·~C + ~B·~C y, por De Morgan, f = (A + B)·(A + C)·(B + C). Mismo costo que A·B + A·C + B·C.
12 · Síntesis
Ideas para recordar
- El mapa es la tabla de verdad en una cuadrícula con orden Gray (00, 01, 11, 10): celdas pegadas difieren en un solo bit.
- Los bordes opuestos también están pegados: el mapa es un toroide (las cuatro esquinas son un grupo).
- Grupos: rectángulos de 1, 2, 4, 8 o 16 celdas, sin diagonales, sin ceros, lo más grandes posible, con superposición permitida.
- Leer un grupo: las variables que cambian desaparecen; las que no cambian quedan (directas si valen 1, negadas si valen 0).
- Primero los unos aislados y los esenciales; después cubrir el resto con los grupos más grandes. Un primo cuyos unos ya están cubiertos sobra.
- X: comodín que agranda grupos; no hay que cubrirlas. Agrupar los ceros y aplicar De Morgan da el producto de sumas.
- Un tablero de unos aislados indica una función tipo XOR: no se simplifica en AND-OR.
- Hasta 4 variables a mano; con 5 o más, mapas apilados o software (Quine-McCluskey, Espresso).
¿Listo para el Tema 17?
Podés avanzar si sos capaz de responder esto sin mirar:
- Escribir el orden Gray de columnas y explicar por qué no es el binario.
- Ubicar el minterm m13 en el mapa de 4 variables.
- Identificar un grupo que cruza el borde y leer su expresión.
- Simplificar un mapa de 3 variables con tres unos y uno compartido.
- Usar las X para agrandar un grupo.
- Obtener un producto de sumas agrupando los ceros.
Ya podés minimizar cualquier función de pocas variables. Ahora hay que usar esa habilidad para construir de verdad: en el próximo tema, diseño combinacional paso a paso, del enunciado al circuito terminado.