Unidad 6 · Los códigos BCH · cuántas palabras hay de cada peso: el dual del (31,21) y MacWilliams, la binomial para los códigos largos, y la probabilidad de error no detectado con sus dos cotas
Un código BCH garantiza su distancia diseñada y no dice nada más. Pero usado como detector, un código falla cuando el canal escribe justo otra palabra, y cuántas veces pasa eso depende de cuántas palabras hay de cada peso. La distribución de pesos de un BCH cualquiera no se conoce en forma cerrada. Hay dos caminos parciales: contarla desde el dual, cuando el dual es chico, y aproximarla con una binomial, cuando el código es largo. De ella sale la probabilidad de error no detectado, que tiene dos cotas.
La distribución de pesos de un código es la lista , donde cuenta las palabras de peso . Se la guarda en un polinomio, el enumerador de pesos:
En un código lineal, el menor con es la distancia mínima. La cota BCH asegura que , y de los demás coeficientes no dice nada.
Para qué sirven. Sobre el canal binario simétrico con probabilidad de transición , se transmite y llega . Si el patrón de error es una palabra no nula, es otra palabra, el síndrome da cero y el error pasa sin que nadie lo vea. Y sólo así: si no es palabra, el síndrome no es cero. Un patrón de peso tiene probabilidad , y hay palabras de ese peso:
Es la probabilidad de error no detectado. Con chico manda el primer término, , y con todos los patrones pesan lo mismo y queda : una palabra no nula cualquiera, sobre todos los vectores.
El (31,21) es el BCH de largo 31 que corrige dos errores. Sobre , con , las cuatro raíces caen en dos clases conjugadas, la de y la de , y el generador es el producto de sus dos polinomios mínimos:
con y . El código tiene palabras. Su dual es un (31,10): tiene 1024. Un BCH de tasa alta tiene un dual chico, y ésa es la asimetría que se aprovecha.
Paso 1: el polinomio de paridad. Como divide a , el cociente es el polinomio de paridad, de grado :
Paso 2: el dual lo genera el recíproco. El dual de un cíclico es cíclico, y su generador es , el mismo polinomio leído al revés (está en el polinomio generador):
Paso 3: contar. Las palabras del dual son los con de grado menor que 10. Recorridas las 1024, los pesos que aparecen son cuatro:
Paso 4: la forma cerrada. Para los BCH primitivos de sentido estricto con y impar, el dual tiene siempre esos tres pesos no nulos, alrededor de , y se sabe cuántas palabras hay de cada uno. Con :
| peso i | con m = 5 | palabras Bᵢ | con m = 5 |
|---|---|---|---|
| 0 | 0 | 1 | 1 |
| 2m−1 − 2(m−1)/2 | 12 | (2m−2 + 2(m−3)/2)(2m − 1) | (8 + 2)·31 = 310 |
| 2m−1 | 16 | (2m−1 + 1)(2m − 1) | 17·31 = 527 |
| 2m−1 + 2(m−1)/2 | 20 | (2m−2 − 2(m−3)/2)(2m − 1) | (8 − 2)·31 = 186 |
El recuento palabra por palabra y la fórmula dan lo mismo, y los cuatro números suman . Para par y para también hay tablas cerradas; para un BCH cualquiera, no.
La identidad de MacWilliams pasa del enumerador del dual al del código:
con la dimensión del código, así que el factor de adelante es uno sobre la cantidad de palabras del dual: acá, . Se comprueba en : y queda , las palabras del (31,21).
Coeficiente a coeficiente: cada palabra del dual de peso aporta , y el coeficiente de en ese producto es el polinomio de Krawtchouk
Con un dual de cuatro pesos, cada es una suma de cuatro términos dividida por 1024. Hechas las 32 sumas:
La lista entera está en la tabla de la sección 04. Enumeradas una por una las 2 097 152 palabras del (31,21), la distribución sale idéntica, coeficiente por coeficiente.
Se lee así. El menor peso no nulo es 5: la distancia mínima real es la diseñada, . La lista es simétrica, , porque la palabra de todos unos está en el código: no es raíz de , así que no lo divide, y es múltiplo suyo. Sumarle esa palabra a otra de peso da una de peso . Por eso al hueco de los pesos 1 a 4 le corresponde otro, del 27 al 30.
Y el hueco sale de una cancelación. En , por ejemplo, los cuatro términos son
El primero, el de la palabra nula del dual, es ; los otros tres, los de las 1023 palabras restantes, lo anulan exacto. En el widget de abajo se ve, peso por peso, qué hace cada término.
El segundo camino es una aproximación. Para un BCH primitivo de largo con bits de paridad, y suficientemente grande,
con acotado por una constante, de modo que el factor de corrección tiende a 1 cuando el código se alarga. De los vectores de peso , al código le toca la fracción que le corresponde por su tamaño: salvo en los pesos bajos, un BCH largo reparte sus palabras como si las hubieran elegido al azar. La estructura algebraica se ve en el hueco, y en nada más.
La binomial no sale de la nada: es el primer término de MacWilliams. La palabra nula del dual tiene y , así que aporta exactamente . Lo demás es la corrección que traen las otras palabras del dual. La aproximación dice que en un BCH largo esa corrección es chica, salvo donde borra los pesos bajos. En el (31,21), con la mitad de la lista, porque la otra mitad es su espejo:
| i | 31 − i | Aᵢ | binomial | razón |
|---|---|---|---|---|
| 0 | 31 | 1 | 0,001 | — |
| 1 | 30 | 0 | 0,030 | 0 |
| 2 | 29 | 0 | 0,454 | 0 |
| 3 | 28 | 0 | 4,390 | 0 |
| 4 | 27 | 0 | 30,728 | 0 |
| 5 | 26 | 186 | 165,929 | 1,121 |
| 6 | 25 | 806 | 719,024 | 1,121 |
| 7 | 24 | 2 635 | 2 567,944 | 1,026 |
| 8 | 23 | 7 905 | 7 703,833 | 1,026 |
| 9 | 22 | 18 910 | 19 687,573 | 0,961 |
| 10 | 21 | 41 602 | 43 312,661 | 0,961 |
| 11 | 20 | 85 560 | 82 687,808 | 1,035 |
| 12 | 19 | 142 600 | 137 813,013 | 1,035 |
| 13 | 18 | 195 300 | 201 419,019 | 0,970 |
| 14 | 17 | 251 100 | 258 967,310 | 0,970 |
| 15 | 16 | 301 971 | 293 496,284 | 1,029 |
La binomial es , y la razón, sobre ella. Del peso 5 al 26 la razón queda entre 0,961 y 1,121, y la peor está en el borde del hueco. Adentro del hueco, la binomial pone 35,6 palabras entre los pesos 1 y 4 —30,7 de peso 4— y el código no tiene ninguna. En un código más corto la aproximación ajusta peor: en el (15,7), con
Ahí la razón va de 0,597, en los pesos 7 y 8, a 1,534, en el 5, el 6, el 9 y el 10.
Arranca en el peso 15, el del medio, donde la barra y la binomial casi se tocan y el primer término se lleva el 97 % de la suma. Bajá hacia el 5 y mirá cómo crecen los otros tres. Después pasá al 4: el primer término sigue ahí, y los otros tres lo anulan.
Con la aproximación de la sección 04 en la fórmula de , los pesos de 1 a no aportan y la redundancia sale como factor:
La suma es la probabilidad de que ocurran más de errores, y no pasa de uno. De ahí la cota general:
La probabilidad de dejar pasar un error queda acotada por la redundancia, sea cual sea el canal. Es la probabilidad de que un vector elegido al azar caiga en el código: . Para el (255,239) vale .
Con chico es muy pesimista. En el (31,21), con la distribución exacta de la sección 03 y ,
65 230 veces menos: 4,8 órdenes de magnitud. Recién en , con un canal que no transporta nada, las dos coinciden: .
La suma de la sección 05 es la cola de una binomial, y la cola se acota con la desigualdad de Chernoff. Con y ,
con la entropía binaria y . Desarmado, el exponente es
que es positivo siempre que , y crece a medida que se aleja de hacia abajo. Juntando con la sección 05:
que decae exponencialmente en el largo además de en la redundancia. En el (31,21), . Las dos cotas contra la cuenta exacta:
| p | exacta | exponencial | exponencial / exacta | 2⁻¹⁰ / exacta |
|---|---|---|---|---|
| 10⁻⁴ | 1,856·10⁻¹⁸ | 8,642·10⁻¹⁸ | 4,66 | 5,3·10¹⁴ |
| 10⁻³ | 1,820·10⁻¹³ | 8,442·10⁻¹³ | 4,64 | 5,4·10⁹ |
| 10⁻² | 1,497·10⁻⁸ | 6,672·10⁻⁸ | 4,46 | 6,5·10⁴ |
| 0,05 | 1,952·10⁻⁵ | 7,135·10⁻⁵ | 3,66 | 50,0 |
| 0,1 | 2,085·10⁻⁴ | 5,598·10⁻⁴ | 2,68 | 4,68 |
| 0,15 | 5,359·10⁻⁴ | 9,618·10⁻⁴ | 1,79 | 1,82 |
La general es plana y la exponencial sigue la pendiente: con chico, va como , igual que el término que manda en la exacta, y queda a un factor 4,5 arriba. Al acercarse a las dos cotas se juntan. Las dos llevan porque se tiró el factor ; en el (31,21) la exponencial queda arriba de la exacta en todo , por un factor de al menos 1,62.
Arranca en , donde la exacta y la cota general son la misma cosa. Bajá y mirá cuánto se abre la distancia, y dónde aparece la exponencial. Apostá antes de ver el (15,7), y después recorré su curva de punta a punta.
El (15,7) es el BCH de largo 15 que corrige dos errores: ocho bits de paridad, dos menos que el (31,21), y la misma distancia mínima, 5. En la zona plana su cota general es contra , y la curva exacta lo sigue: en el (15,7) deja pasar 3,97 veces más que el (31,21), que es salvo por el de .
Pero el orden se invierte. Las dos curvas exactas se cruzan en , y debajo de ahí el (31,21), con más redundancia, deja pasar más: en , contra , 10,2 veces más por palabra. Con chico manda , y el (31,21) tiene 186 palabras de peso 5 donde el (15,7) tiene 18: una palabra más larga ofrece más maneras de que cinco errores formen otra palabra. La redundancia fija el techo; el piso lo fijan la distancia mínima y cuántas palabras hay a esa distancia. La cota general no ve ninguna de las dos.
El no es un adorno. sale de suponer la binomial, y un código que se aparta mucho de ella puede pasarla. El (15,5) de , un cíclico que no es BCH, repite tres veces un bloque de cinco bits: . Barriendo de en hasta , su pasa a por un factor de hasta 2,91, en .
Los dos BCH de la sección 07 no la pasan en ningún punto de ese barrido: el máximo de está en y vale . Para saberlo de un código hay que tener su distribución, y ése es todo el punto: la cota BCH no la da, el dual la da sólo cuando es chico, y la binomial la adivina sólo cuando el código es largo. Lo que pasa con un error más que , cuando el código corrige en vez de detectar, también lo deciden las palabras de peso mínimo: está en pasado t, la cuenta la hacen las palabras livianas.
El camino entero para el (31,21), con enteros como polinomios binarios: el bit es el coeficiente de . Divide para sacar , lo invierte, recorre el dual, aplica MacWilliams con los polinomios de Krawtchouk y evalúa . Lo de los comentarios es la salida de correrlo.
from math import comb
def mul(a, b):
"""Producto de polinomios binarios guardados como enteros (bit i = X^i)."""
r = 0
while b:
if b & 1:
r ^= a
a, b = a << 1, b >> 1
return r
def div(a, b):
q = 0
while a.bit_length() >= b.bit_length():
s = a.bit_length() - b.bit_length()
q, a = q | 1 << s, a ^ b << s
return q, a
n, k = 31, 21
g = 0b11101101001 # 1 + X³ + X⁵ + X⁶ + X⁸ + X⁹ + X¹⁰
h, resto = div(1 << n | 1, g) # h(X) = (X³¹ + 1) / g(X)
hr = int(f'{h:0{k + 1}b}'[::-1], 2) # h*(X) = X²¹ h(1/X)
B = [0] * (n + 1) # el dual: los 1024 múltiplos de h*(X)
for u in range(1 << (n - k)):
B[bin(mul(u, hr)).count('1')] += 1
print(resto, {i: b for i, b in enumerate(B) if b})
# 0 {0: 1, 12: 310, 16: 527, 20: 186}
def K(j, i): # Krawtchouk: el coeficiente de z^j
return sum((-1) ** l * comb(i, l) * comb(n - i, j - l) for l in range(j + 1))
S = [sum(B[i] * K(j, i) for i in range(n + 1)) for j in range(n + 1)]
A = [s // 2 ** (n - k) for s in S]
print(A[:9], sum(A) == 2 ** k, all(s % 2 ** (n - k) == 0 for s in S))
# [1, 0, 0, 0, 0, 186, 806, 2635, 7905] True True
Pu = lambda p: sum(A[i] * p ** i * (1 - p) ** (n - i) for i in range(1, n + 1))
print(f'{Pu(1e-2):.4e} {2.0 ** -(n - k):.4e} {Pu(0.5) * 2 ** (n - k):.7f}')
# 1.4971e-08 9.7656e-04 0.9999995
El 0 del primer renglón es el resto de la división: divide a . El segundo True dice que las 32 sumas son múltiplos exactos de 1024, así que la división entera no redondea nada. El 0.9999995 del final es .