Unidad 6 · Los códigos BCH · un borrado cuesta medio error: la cota, el procedimiento de dos pasadas que la alcanza sin tocar el decodificador, por qué una de las dos acierta y por qué el criterio la encuentra, y lo que pasa un paso más allá
Un demodulador que decide bit por bit fuerza un valor aun cuando la muestra cayó pegada al umbral. Si en esos casos dice «no sé», el decodificador recibe tres símbolos: cero, uno y borrado. Un error obliga a averiguar dos cosas, dónde está y cuánto vale. Un borrado sólo una: dónde está ya se sabe. Por eso un borrado cuesta la mitad que un error, y en un código BCH binario se puede aprovechar sin cambiar una sola línea del decodificador.
Llega una palabra con errores, de posición desconocida, y borrados, de posición conocida. Un código de distancia mínima recupera la palabra transmitida siempre que
Con , alcanza con la forma que usa la capacidad de corrección:
Cada borrado gasta media unidad del presupuesto , y cada error una entera. Los casos extremos son los conocidos: sin borrados queda , y sin errores se rellenan hasta borrados. En el (15,5), con y , los pares que entran justo son estos:
| errores ν | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| borrados ϵ, como mucho | 6 | 4 | 2 | 0 |
La cuenta vale para cualquier código, binario o no. Sean la palabra transmitida y el conjunto de las posiciones borradas.
Paso 1: tachar los borrados. Se borran las posiciones de en todas las palabras del código. Queda un código de largo y lo recibido, tachado igual, tiene sólo los errores.
Paso 2: tachar cuesta a lo sumo ϵ de distancia. Dos palabras distintas difieren en al menos posiciones, y tachar les saca a lo sumo de ellas. El código tachado tiene distancia .
Paso 3: los ν errores se corrigen ahí. Un código de distancia corrige errores si , que es la cota con pasado al otro lado.
Paso 4: completar es único. Como , dos palabras del código no pueden coincidir fuera de : la palabra tachada determina la palabra entera.
Un error se paga dos veces porque hay que alejarse de él por los dos lados: la palabra recibida queda a de la transmitida y tiene que quedar a más de de cualquier otra. Un borrado no aleja a lo recibido de nada, porque en esa posición no hay valor que comparar: sólo achica el espacio donde las palabras se distinguen.
La prueba de la sección 02 pide decodificar un código tachado, que es otro código para cada . En un código binario no hace falta: el valor de un borrado sólo puede ser 0 o 1, y se prueban los dos rellenos con el decodificador ordinario de errores, el de los síndromes, Berlekamp-Massey y la búsqueda de Chien, sin modificarlo.
Paso 1: rellenar con ceros. Las posiciones borradas se ponen en 0 y se decodifica. Sale una palabra , o el decodificador declara falla.
Paso 2: rellenar con unos. Las mismas posiciones se ponen en 1 y se decodifica de nuevo. Sale , o una falla.
Paso 3: elegir. Entre las pasadas que entregaron una palabra, gana la que corrigió menos posiciones fuera de . Una pasada que declaró falla no compite. Si las dos fallan, se declara falla.
Lo que se corrigió adentro de no cuenta: ahí el relleno fue una apuesta, y que el decodificador la dé vuelta no dice nada sobre el canal. Lo de afuera sí: cada corrección fuera de es un error que el canal tuvo que cometer para que esa palabra fuera la transmitida.
Sea la cantidad de posiciones borradas en las que vale 1.
Paso 1: cuántos errores mete cada relleno. Rellenar con ceros se equivoca justo en esas posiciones, y rellenar con unos en las otras . Los dos vectores llevan además los errores de afuera: errores en total con ceros, con unos.
Paso 2: si ϵ* ≤ ϵ/2, acierta la de ceros. Tiene errores, y el decodificador corrige cualquier patrón de hasta .
Paso 3: si ϵ* > ϵ/2, acierta la de unos. Tiene .
Las dos pueden acertar a la vez: si queda cerca de la mitad, los dos rellenos tienen a lo sumo errores. Lo que la prueba garantiza es que nunca fallan las dos.
Que una pasada acierte no sirve si no se sabe cuál. Sea la buena la que entrega . Lo que haga la otra tiene tres formas, y el paso 3 tiene que funcionar en las tres.
Paso 1: la buena corrige exactamente ν afuera. Fuera de lo recibido difiere de justo en los errores del canal.
Paso 2: si la otra también entrega v, da igual cuál se elija.
Paso 3: si la otra declara falla, no compite. Una falla no corrigió nada, ni adentro ni afuera. Si se la contara como cero correcciones, le ganaría a la buena cada vez que , y el procedimiento declararía falla con la palabra correcta en la mano. Por eso el paso 3 elige entre las pasadas que entregaron una palabra, y no pasa poco: en el (15,5) la otra pasada falla en uno de cada cuatro casos, como se mide en la sección 06.
Paso 4: si la otra entrega otra palabra w, corrige más afuera. Sea la cantidad de posiciones que corrigió fuera de . Fuera de , difiere de lo recibido en posiciones y en , así que y difieren ahí en a lo sumo . Adentro de difieren en a lo sumo . Como son dos palabras distintas,
La última desigualdad es la cota. La palabra equivocada corrige afuera por lo menos posiciones, una más que la buena, y nunca empata con ella. El margen es : se achica a medida que la cota se aprieta, y en el borde es uno.
Arranca con un error y dos borrados, lejos del borde. Apostá y decodificá. Después borrá también las posiciones 6 y 8, que en valen uno, y volvé a apostar; con esa misma palabra, pasá el criterio a «cuenta cero correcciones». Cargá «otra palabra» y mirá cuántas corrige afuera cada pasada. Y al final cargá el borde: los siete borrados caen justo sobre los siete unos de .
Con , la cota pide . Se recorrieron todos los casos: las 32 palabras del código como transmitidas, todos los conjuntos de borrados y todos los patrones de errores fuera de ellos, con el decodificador de síndromes, Berlekamp-Massey binario y Chien. Son 42 129 pares de patrones por palabra. Agrupados por , lo que hace la pasada que no es la buena:
| 2ν + ϵ | casos | las dos dan v | la otra falla | la otra da otra palabra | elegida = v |
|---|---|---|---|---|---|
| 0 a 3 | 25 632 | 25 632 | 0 | 0 | 25 632 |
| 4 | 90 720 | 84 840 | 3360 | 2520 | 90 720 |
| 5 | 314 496 | 239 400 | 42 336 | 32 760 | 314 496 |
| 6 | 917 280 | 398 440 | 291 200 | 227 640 | 917 280 |
| total | 1 348 128 | 748 312 | 336 896 | 262 920 | 1 348 128 |
El procedimiento acierta en los 1 348 128. Debajo de las dos pasadas tienen a lo sumo tres errores y aciertan siempre las dos. Desde ahí, la otra pasada se equivoca cada vez más: en el total, en 336 896 casos declara falla —uno de cada cuatro— y en 262 920 entrega otra palabra.
Las tres formas del paso 3, medidas una por una:
Y la segunda pasada no es un lujo: rellenando sólo con ceros se recupera en 1 048 220 de los 1 348 128 casos, el 77,8 %. Sólo con unos, en la misma cantidad, porque la palabra de todos unos está en el código y cambia un relleno por el otro.
Con la cota deja de valer, pero es impar, así que no puede ser la mitad exacta: uno de los dos rellenos mete a lo sumo errores y su pasada tiene . Una de las dos acierta siempre. Lo que se cae es el paso 4: el margen llega a cero, y la otra puede entregar una palabra con las mismas correcciones afuera que la buena.
Eso no es una falla del criterio. Si y explican lo recibido con errores cada una y los borrados no dicen nada, no hay dato que las distinga: es la ambigüedad que la cota excluye. Pasa justo cuando es una palabra de peso 7 que cubre los borrados, los errores y otras posiciones. El (15,5) tiene 15 palabras de peso 7, y la cuenta es cerrada:
El recorrido da el mismo número. Con aparece además lo que la sección 04 excluía: con par y , los dos rellenos tienen cuatro errores y ninguna pasada acierta. Con el paso 3 tal cual, y un empate entre palabras distintas declarado como falla:
| 2ν + ϵ | casos | alguna da v | entrega v | empate | otra palabra | falla |
|---|---|---|---|---|---|---|
| ≤ 6 | 1 348 128 | 100 % | 100 % | 0 | 0 | 0 |
| 7 | 2 302 560 | 100 % | 91,8 % | 8,2 % | 0 | 0 |
| 8 | 5 054 400 | 58,5 % | 47,0 % | 12,3 % | 25,6 % | 15,2 % |
En 7 nada sale mal en silencio: o se entrega , o se sabe que hay dos candidatas igual de buenas. Si el empate se rompe eligiendo la pasada de ceros, como haría un circuito que compara con , la mitad de los 188 640 sale con la palabra equivocada: 94 320. En 8, un cuarto de los casos entrega otra palabra sin aviso, la misma decodificación errónea que con errores. La cota es exacta en los dos sentidos: adentro no se escapa nada, y en el primer paso afuera ya hay casos que ningún criterio resuelve.
El mismo recorrido, con otro decodificador: uno de tabla, que a cada vector a distancia de una palabra le asigna esa palabra y declara falla con el resto. No comparte una línea con el decodificador algebraico, y da lo mismo que él en cada fila: la última columna de la sección 06 y las cuatro últimas de la 07. Lo de los comentarios es la salida de correrlo; tarda medio minuto.
from itertools import combinations
n, t = 15, 3
g = 0b10100110111 # g(X) = 1 + X + X² + X⁴ + X⁵ + X⁸ + X¹⁰
def mul(a, b): # producto de polinomios binarios
r = 0
while b:
if b & 1:
r ^= a
a, b = a << 1, b >> 1
return r
C = [mul(m, g) for m in range(32)] # las 32 palabras del (15,5)
bits = lambda ps: sum(1 << p for p in ps)
# Decodificador de distancia acotada: a cada vector a distancia ≤ t de una
# palabra, esa palabra. Lo que no está en la tabla es una falla declarada.
tabla = {}
for c in C:
for w in range(t + 1):
for ps in combinations(range(n), w):
tabla[c ^ bits(ps)] = c
def dos_pasadas(r, E):
m = bits(E)
candidatas = []
for relleno in (r & ~m, r | m): # los borrados en cero, después en uno
w = tabla.get(relleno)
if w is not None: # una falla no compite
candidatas.append((bin((w ^ r) & ~m).count('1'), w))
if not candidatas:
return 'falla'
candidatas.sort()
if len(candidatas) == 2 and candidatas[0][0] == candidatas[1][0] \
and candidatas[0][1] != candidatas[1][1]:
return 'empate'
return candidatas[0][1]
for s in range(9): # s = 2ν + ϵ
cuenta = {'v': 0, 'otra': 0, 'empate': 0, 'falla': 0}
for nu in range(s // 2 + 1):
for E in combinations(range(n), s - 2 * nu):
afuera = [i for i in range(n) if i not in E]
for errores in combinations(afuera, nu):
e = bits(errores)
for v in C:
x = dos_pasadas(v ^ e, E)
clase = 'v' if x == v else x if isinstance(x, str) else 'otra'
cuenta[clase] += 1
print(s, sum(cuenta.values()), cuenta)
# 0 32 {'v': 32, 'otra': 0, 'empate': 0, 'falla': 0}
# 1 480 {'v': 480, 'otra': 0, 'empate': 0, 'falla': 0}
# 2 3840 {'v': 3840, 'otra': 0, 'empate': 0, 'falla': 0}
# 3 21280 {'v': 21280, 'otra': 0, 'empate': 0, 'falla': 0}
# 4 90720 {'v': 90720, 'otra': 0, 'empate': 0, 'falla': 0}
# 5 314496 {'v': 314496, 'otra': 0, 'empate': 0, 'falla': 0}
# 6 917280 {'v': 917280, 'otra': 0, 'empate': 0, 'falla': 0}
# 7 2302560 {'v': 2113920, 'otra': 0, 'empate': 188640, 'falla': 0}
# 8 5054400 {'v': 2376360, 'otra': 1292760, 'empate': 619200, 'falla': 766080}
Las dos líneas que hacen al paso 3 son if w is not None, que deja afuera a la pasada que falla, y la que declara empate cuando las dos candidatas son palabras distintas con el mismo número de correcciones afuera. La máscara ~m es lo que cuenta sólo fuera de los borrados.