Unidad 3 · Códigos de bloque lineales · peso y distancia, el peso mínimo, las columnas de H, la distribución de pesos, la capacidad de corrección, las esferas y los modos de operación
Un código sirve si sus palabras están lejos unas de otras: para que el canal convierta una en otra tiene que dar vuelta muchos bits a la vez. La distancia mínima mide cuán lejos están las dos más cercanas, y de ese solo número salen cuántos errores se detectan y cuántos se corrigen con garantía.
El peso de Hamming de un vector de , , es la cantidad de componentes no nulas. La distancia de Hamming entre dos vectores, , es la cantidad de posiciones en que difieren. Las dos se tocan en una identidad:
La suma módulo 2 pone un 1 exactamente donde los dos vectores difieren. Con dos palabras del : 1000 110 y 0100 011 suman 1100 101, de peso 4, y difieren en cuatro posiciones.
La distancia es una métrica de verdad: no es negativa, es simétrica, vale cero sólo entre un vector y sí mismo, y cumple la desigualdad triangular, . Por eso vale la imagen que se usa todo el tiempo: los vectores son los vértices de un hipercubo de dimensiones, la distancia es la cantidad de aristas para ir de uno a otro, y las palabras de código son un subconjunto de vértices que conviene tener separados.
La distancia mínima de un código es la menor distancia entre dos palabras distintas:
Es tan central que un código se nombra por la terna . Por la definición hay que comparar todos los pares: en el son . Contados uno por uno, 56 pares están a distancia 3, 56 a distancia 4 y 8 a distancia 7. En un código lineal alcanza con mucho menos: si es el menor peso de las palabras no nulas,
Paso 1: cada distancia es un peso. Por la identidad de arriba, , y como el código es lineal, es otra palabra, no nula porque .
Paso 2: cada peso es una distancia. El peso de una palabra no nula es su distancia a la palabra nula, , y la nula está en todo código lineal.
Paso 3: mismos conjuntos, mismo mínimo. Los números sobre los que se toman los dos mínimos coinciden, así que los mínimos coinciden.
El costo pasa de los pares a las palabras: 15 pesos en vez de 120 comparaciones. Sigue siendo exponencial en , y hay un camino mejor, que mira la matriz de paridad en vez de las palabras. La misma cuenta dice algo más: vistas desde cualquier palabra, las demás están a las mismas distancias que vistas desde la nula. Por eso las 120 distancias son los 15 pesos vistos desde cada una de las 16 palabras, con cada par contado dos veces: los 56 pares a distancia 3 son . En un código lineal ninguna palabra está peor rodeada que otra.
es el primer peso no nulo, pero para calcular probabilidades hace falta saber cuántas palabras hay de cada peso. La distribución de pesos es la lista , con la cantidad de palabras de peso . Siempre y , y se la junta en el polinomio enumerador .
| código | A₀ | A₁ | A₂ | A₃ | A₄ | A₅ | A₆ | A₇ | A₈ | dmin |
|---|---|---|---|---|---|---|---|---|---|---|
| (7,4) | 1 | 0 | 0 | 7 | 7 | 0 | 0 | 1 | · | 3 |
| (8,4) | 1 | 0 | 0 | 0 | 14 | 0 | 0 | 0 | 1 | 4 |
El es el de la Tabla 1 del apunte, con . El es el mismo con un bit más de paridad global, que vuelve par toda palabra: las de peso 3 pasan a 4, las de peso 4 se quedan, y la distancia mínima sube a 4.
Lo que importa del primer coeficiente no nulo es su tamaño. Sobre el BSC con probabilidad de bit chica, un patrón de error pasa inadvertido sólo si es una palabra, y la cuenta la dominan las más livianas: . En el , ; con la cuenta exacta da . Dos códigos con la misma pueden diferir en ese coeficiente, y la distancia mínima sola no lo ve.
Calcular la distribución de un código grande es difícil en general. La identidad de MacWilliams la saca de la del código dual, que conviene cuando el dual tiene pocas palabras.
Con escrita por columnas, el producto es la suma de las columnas donde tiene un 1, y es palabra si y sólo si esa suma da cero. Juntando las dos cosas:
Hay una palabra de peso con unos en las posiciones si y sólo si . Entonces es la menor cantidad de columnas de que suman cero, o sea, que son linealmente dependientes: sobre los únicos coeficientes no nulos son unos, y «dependientes» es «alguna suma da cero». Y hay más: la cantidad de conjuntos de columnas que suman cero es exactamente , porque cada conjunto es una palabra.
De ahí salen dos criterios que se aplican mirando:
Las siete columnas de la del son 110 011 111 101 100 010 001: los siete vectores no nulos de , todos distintos. Entonces . Y no puede ser más, porque con todos los no nulos de presentes, la suma de dos columnas cualesquiera es otra columna: , y la palabra con unos en 0, 1 y 3 es 1101 000.
El criterio también sirve para diseñar. Un código con y bits de paridad es una elección de columnas distintas y no nulas de , y como hay , el largo queda acotado: . Usarlas todas es la familia de Hamming. Para llegar a 4 hace falta que tampoco sume cero ninguna terna. Las ocho columnas del son 1101 0111 1110 1011 1000 0100 0010 0001, todas de peso impar, y tres vectores de peso impar suman uno de peso impar, que nunca es cero.
Una columna sola no da cero, y dos tampoco: el widget lo dice en cuanto lo probás, y son los dos criterios de recién. En el la primera terna que suma cero aparece enseguida; hay siete, una por cada palabra de peso 3. Elegir las siete columnas también da cero: es 1111 111. En el ninguna terna sirve, por la paridad de los pesos, y el conjunto más chico tiene cuatro columnas: hay catorce, una por cada palabra de peso 4.
El receptor detecta un error cuando lo recibido no es palabra, o sea cuando el síndrome no es nulo. El error pasa inadvertido sólo si el patrón es él mismo una palabra no nula, y ninguna pesa menos que :
Es una garantía de peor caso. Por encima de no se promete nada, pero casi todo se detecta igual: de los patrones no nulos se escapan sólo los que son palabras. En el , 15 de 127, y cuáles son lo cuenta un síndrome cero no prueba nada.
Corregir pide más que detectar: hay que saber qué palabra se mandó. Alrededor de cada palabra se traza una esfera de radio , los vectores a distancia de ella. Si las esferas no se tocan, todo vector recibido con errores o menos cae en la esfera de la palabra transmitida y en ninguna otra, y el decodificador la identifica sin ambigüedad.
Paso 1: si se tocan, las palabras están cerca. Si está en las esferas de y de , la desigualdad triangular da .
Paso 2: y al revés. Si dos palabras están a , dando vuelta en la primera de las posiciones en que difieren se llega a un vector que está a de las dos.
Paso 3: la condición. Las esferas no se tocan si y sólo si , y el mayor que lo cumple es la capacidad de corrección:
El y el tienen los dos : el bit que el agrega no corrige nada más. Lo que cambia es el espacio que dejan las esferas. En el cada una tiene vectores, y las 16 juntan 128: todo , sin hueco. Es un código perfecto. En el cada esfera tiene 9 y las 16 juntan 144 de los 256. Los otros 112 no están en ninguna: cada uno está a distancia 2 de exactamente cuatro palabras. Con par sobra siempre esa capa de vectores equidistantes, y sirve para detectar.
Detectar y corregir se reparten la misma . El decodificador del modo mixto corrige si lo recibido cae a distancia de alguna palabra y, si no, levanta una bandera. Así corrige hasta errores y detecta, sin corregir mal, hasta , con , siempre que
Paso 1: hasta λ errores, corrige. Como , la condición implica , o sea : las esferas de radio no se tocan, y lo recibido cae sólo en la de la palabra transmitida.
Paso 2: entre λ + 1 y ℓ errores, avisa. Lo recibido está a más de de la palabra transmitida . Y de cualquier otra palabra , por la desigualdad triangular, . No cae en ninguna esfera de radio , y el decodificador levanta la bandera en vez de corregir hacia otra palabra.
Los dos modos puros son los extremos. Con no se corrige nada y se detecta hasta : es la sección 05. Con se corrige todo lo que se puede y no se detecta nada más allá: es la sección 06. En el medio, cada error más que se corrige es uno menos que se detecta con garantía. Los pares que no se pueden mejorar en ninguna de las dos coordenadas:
| dmin | detectar | mixto | corregir |
|---|---|---|---|
| 3 | (0, 2) | — | (1, 1) |
| 4 | (0, 3) | — | (1, 2) |
| 5 | (0, 4) | (1, 3) | (2, 2) |
Con par el modo de corrección ya trae detección gratis: el corrige 1 y detecta 2, y eso es la capa de 112 vectores de la sección anterior. El modo mixto es el que se usa cuando hay canal de retorno: los errores leves se corrigen en el momento y los graves se mandan a retransmitir. El decodificador que lo implementa con es la decodificación incompleta.
La condición dice qué pasa hasta errores y nada más. Un error de más y el decodificador puede corregir mal sin avisar. Transmitiendo la palabra nula por el en el modo y barriendo los 256 patrones de error, por peso: cuántos corrige bien, en cuántos avisa, cuántos corrige hacia otra palabra y cuántos no ve.
| w(e) | patrones | corrige | avisa | yerra | no ve |
|---|---|---|---|---|---|
| 1 | 8 | 8 | 0 | 0 | 0 |
| 2 | 28 | 0 | 28 | 0 | 0 |
| 3 | 56 | 0 | 0 | 56 | 0 |
| 4 | 70 | 0 | 56 | 0 | 14 |
Hasta dos errores la garantía se cumple entera. Con tres se rompe entera: los 56 patrones de peso 3 quedan a distancia 1 de una palabra de peso 4, el decodificador los «corrige» hacia ella y entrega una palabra equivocada sin bandera. Con cuatro vuelve a avisar en 56, y los 14 que son palabras pasan sin que el síndrome los vea.
El lo muestra con un error menos. Su único modo que corrige es , sin margen para detectar, y los 21 patrones dobles se corrigen mal: con dos errores el decodificador empeora. Por eso en un código perfecto la bandera no tiene dónde levantarse: en un código perfecto la bandera no se levanta.
La búsqueda del widget, escrita entera: recorre los conjuntos de columnas por tamaño creciente y devuelve el primero que suma cero. Las son la del apunte para el y la del en la misma forma . Lo de los comentarios es la salida de correrlo.
from itertools import combinations
def dmin_por_columnas(H):
"""La menor cantidad de columnas de H que suman cero, y cuáles."""
m, n = len(H), len(H[0])
columnas = [tuple(fila[i] for fila in H) for i in range(n)]
for w in range(1, n + 1):
for elegidas in combinations(range(n), w):
suma = [sum(columnas[i][j] for i in elegidas) % 2 for j in range(m)]
if not any(suma):
return w, elegidas
H74 = [[1, 0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 0, 0, 1]]
H84 = [[1, 0, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 0, 0, 1, 0],
[1, 1, 0, 1, 0, 0, 0, 1]]
print(dmin_por_columnas(H74)) # (3, (0, 1, 3))
print(dmin_por_columnas(H84)) # (4, (0, 1, 2, 5))
Recorre conjuntos en el peor caso: depende de y de , no de . En el no gana nada: hasta 3 columnas son 63 conjuntos contra 16 palabras. Gana cuando es grande y chica: en el Hamming , hasta 3 columnas son 2.763.775 conjuntos, y las palabras son . Con grande deja de servir: el problema general es difícil, y por eso las familias de las unidades que siguen se construyen para que se sepa de antemano.