Unidad 6 · Los códigos BCH · el localizador como el registro más corto que genera los síndromes: la discrepancia, la elección de ρ, la versión binaria de t iteraciones, la Tabla 6 fila por fila y la versión sin inversiones
Los síndromes de un código BCH dan un sistema lineal para los coeficientes del polinomio localizador. El método de Peterson lo resuelve con determinantes, y su costo crece como . El algoritmo iterativo llega al mismo en del orden de operaciones y sin un solo determinante, porque mira el problema de otra manera: busca el registro de desplazamiento más corto que genera los síndromes. E. R. Berlekamp lo presentó en 1965 para los BCH binarios, y J. L. Massey lo leyó como síntesis de registros en 1969.
Sea un código BCH binario de sentido estricto que corrige errores, con síndromes . Si hubo errores con números localizadores , el localizador es
y las identidades de Newton, para , se escriben como una recurrencia:
Cada síndrome es una combinación de los anteriores, con coeficientes fijos. Es lo que hace un LFSR de etapas con polinomio de conexión : cargado con , saca el resto de la sucesión. La única diferencia con los de la Unidad 5 es que las etapas guardan elementos de , no bits.
Encontrar pasa a ser un problema de síntesis: el registro más corto que genera . Más corto, porque el patrón más probable es el de menos errores, y el grado de es el número de errores. El algoritmo lo arma de a una identidad por vez: arranca con un registro que genera el primer síndrome y, en cada paso, lo corrige lo mínimo indispensable para que genere también el siguiente.
El algoritmo produce una sucesión de polinomios , cada uno el de menor grado que satisface las primeras identidades. Su grado es . Para pasar al siguiente se mide cuánto falla en la identidad número . Esa medida es la discrepancia:
Es lo que el registro predice para , sumado a lo que vale. Si , el registro acierta y pasa igual: , . Si no, se busca un paso anterior con y lo más grande posible, y se corrige con él:
y el grado pasa a ser
Por qué el término agregado arregla la identidad sin romper las anteriores:
Paso 1: correr es atrasar la identidad. Multiplicar por corre sus coeficientes lugares. Lo que el polinomio corrido aporta a la identidad es lo que aportaba a la identidad .
Paso 2: en las identidades viejas aporta cero. Para , la identidad atrasada tiene número , y las cumplía todas. Lo que ya acertaba, lo sigue acertando.
Paso 3: en la nueva aporta exactamente la discrepancia. Para , la identidad atrasada es la , donde fallaba por . Escalado por , aporta , que se cancela con la discrepancia de . En característica dos, sumar es restar.
Cualquier con arregla la identidad. La elección decide el grado: es más chico cuanto más grande es . Massey probó que con esa elección el registro es, en cada paso, el más corto posible: su largo cumple , y no hay registro más corto que genere esos términos. Al cabo de pasos, si hubo a lo sumo errores, es el localizador.
El cálculo se ordena en una tabla con una fila por paso: , , , y , la columna que sirve para elegir . Las dos primeras filas no son iteraciones, son el arranque: con y , que existe para que el primer paso tenga un del cual tirar; y con y .
En un código binario los síndromes pares son cuadrados: . Con eso se prueba que, si cumple las identidades hasta un orden par, cumple también la siguiente: la discrepancia de todo paso de índice impar es cero, y ese paso copia la fila. Se ve en la forma general aplicada al (15,5) con errores en las posiciones 3, 5 y 12:
| μ | σ(X) | dμ | lμ | μ − lμ | ρ |
|---|---|---|---|---|---|
| −1 | 1 | 1 | 0 | −1 | — |
| 0 | 1 | 1 | 0 | 0 | — |
| 1 | 1 + X | 0 | 1 | 0 | −1 |
| 2 | 1 + X | α⁵ | 1 | 1 | — |
| 3 | 1 + X + α⁵X² | 0 | 2 | 1 | 0 |
| 4 | 1 + X + α⁵X² | α¹⁰ | 2 | 2 | — |
| 5 | 1 + X + α⁵X³ | 0 | 3 | 2 | 2 |
| 6 | 1 + X + α⁵X³ | — | 3 | 3 | — |
La columna dice con qué fila se armó cada polinomio. Las filas 1, 3 y 5 tienen discrepancia nula, y no es suerte de este patrón: con la forma general corrida sobre los 575 patrones de uno, dos y tres errores del (15,5), las discrepancias de índice impar dieron cero en todos, y el final fue el mismo que el de la versión binaria.
La versión binaria recorre sólo los pasos pares. Se los reindexa con , y el algoritmo termina en iteraciones en lugar de . Cambian dos cosas. La discrepancia se calcula contra los síndromes impares:
Y la corrección se corre el doble, con elegido por el mayor :
El arranque es , con , , , y , con y . El medio del primero es para que el corrimiento desde él dé un número impar: es la fila de la forma general, contada en pasos dobles. La simplificación vale sólo para códigos binarios. Para uno con símbolos en , como un Reed-Solomon, deja de valer y hay que dar los pasos.
El (15,5) corrige errores sobre , con . Se transmite la palabra nula y llega . Los seis síndromes son
con los pares como cuadrados de los impares: . El algoritmo binario da tres iteraciones:
Fila 0. . El único paso previo con discrepancia no nula es , y el corrimiento es : , de grado 1.
Fila 1. . Los candidatos son , con , y , con . Gana el segundo, corrimiento 2: .
Fila 2. , porque cancela el 1. Ahora gana , con ; el factor es y el corrimiento 2:
| μ | σ(X) | dμ | lμ | 2μ − lμ | ρ |
|---|---|---|---|---|---|
| −½ | 1 | 1 | 0 | −1 | — |
| 0 | 1 | 1 | 0 | 0 | — |
| 1 | 1 + X | α⁵ | 1 | 1 | −½ |
| 2 | 1 + X + α⁵X² | α¹⁰ | 2 | 2 | 0 |
| 3 | 1 + X + α⁵X³ | — | 3 | 3 | 1 |
Son las filas pares de la tabla de la forma general, renumeradas. El grado final, 3, es el número de errores. Las raíces de son , y ; sus inversos, , y , son los localizadores, y dan las posiciones 12, 5 y 3. Encontrarlas es la búsqueda de Chien. Peterson, con su fórmula cerrada para , da el mismo polinomio; y comparados en los 575 patrones de hasta tres errores del (15,5), los dos coinciden siempre.
Antes de usar el widget, pensá la fila 1 de otra manera: ¿qué pasa si ahí se elige , el candidato que pierde? ¿Y si el que se elige mal es el de la fila 2?
Arranca con el patrón de la Tabla 6 y sólo las dos filas del arranque. Hacela entera eligiendo vos. Después volvé a empezar y, en la fila 2, elegí ; y otra vez con en la fila 1. Probá con dos errores, en las posiciones 4 y 9, y con cuatro. Al final, prendé «sin inversiones» y mirá qué cambia y qué no.
es el largo del registro, y sólo crece. Al terminar es el grado de , y el decodificador lo lee como el número de errores. La columna está para elegir de un vistazo: entre las filas con , la del número más grande.
Con menos errores que , las últimas discrepancias son cero. Con errores en 4 y 9, , y : la última iteración copia , de grado 2. Un circuito de latencia fija la hace igual.
Elegir mal no siempre se nota, y por eso el criterio importa. Sobre el patrón de la Tabla 6:
| elección | σ(X) final | grado | raíces dan |
|---|---|---|---|
| la del algoritmo | 1 + X + α⁵X³ | 3 | 3, 5, 12 |
| ρ = −½ en la fila 1 | 1 + X + α⁵X³ | 3 | 3, 5, 12 |
| ρ = 0 en la fila 2 | 1 + X + α⁵X² + α¹⁰X⁴ | 4 | ninguna |
| ρ = −½ en la fila 2 | 1 + X + α⁵X² + α¹⁰X⁵ | 5 | 3, 5, 10, 12 |
El primer desvío llega bien por casualidad. Con en la fila 1 el corrimiento es 3, y sale : genera los primeros cuatro síndromes, pero con donde alcanzaba 2. Ese registro de más resultó ser el localizador, y . En la fila 2 ya no hay margen: un registro más largo que el necesario es un que no es el localizador, y el decodificador o declara falla o, con el de grado 5, encuentra una raíz que no es un error.
La corrección lleva el factor : una inversión en en cada iteración con discrepancia no nula, dentro del lazo. En hardware la inversión es la operación lenta, y queda en el camino crítico. La salida es que a se lo puede multiplicar por cualquier constante no nula sin que cambien sus raíces. Multiplicando la actualización por :
Sólo productos. Esta versión, de H. O. Burton en 1971, cambia la inversión y el producto del factor por un producto más en cada coeficiente de : todos los coeficientes hacen la misma cuenta, y por eso se arma como una fila de celdas iguales.
Que todo siga cerrando tiene un paso no evidente: las filas guardadas también quedan escaladas, cada una por su propio factor, y la discrepancia, que es lineal en , se escala con el mismo factor que su fila. Si la fila tiene factor y la tiene , la cuenta sin inversiones da
el polinomio con inversiones por un factor no nulo. En la Tabla 6 las dos primeras correcciones salen con y no cambian nada; la tercera usa :
En el ejemplo integral, el de la Tabla 8, el factor se acumula antes: la discrepancia de la fila 2 sale en lugar de , porque la fila ya venía multiplicada por , y el polinomio final es veces el de la tabla. Comparadas las dos versiones en los 575 patrones de uno, dos y tres errores del (15,5), el polinomio sin inversiones fue el otro por una constante no nula en todos, con las mismas raíces. Lo mismo en los 120 patrones del (15,7), los 496 del (31,21) y los 4991 del (31,16).
El teorema de Massey promete el registro más corto, no el localizador. Con más de errores, el registro más corto que genera los síndromes suele ser otro, y el algoritmo lo entrega igual, sin aviso. Lo que queda es mirar el resultado: el decodificador declara falla si el grado pasa de , o si Chien no encuentra tantas raíces distintas como el grado. En los 1365 patrones de cuatro errores del (15,5):
| grado de σ | raíces | patrones | lo que pasa |
|---|---|---|---|
| 4 | 1 o 4 | 90 | falla, por el grado |
| 3 | 0 | 750 | falla, por las raíces |
| 3 | 3 | 525 | corrige a otra palabra |
Los 840 declarados y los 525 entregados en silencio son el mismo reparto que da cualquier decodificación incompleta de este código: los 525 son los patrones a distancia 3 de alguna de las 15 palabras de peso 7, y para ellos el registro más corto es un localizador legítimo, el de esa otra palabra. Entre los 90 de grado 4 hay 15 en que las cuatro raíces son justo las cuatro posiciones erradas, y el decodificador falla igual: con seis síndromes no hay garantía de que un registro de cuatro etapas sea el bueno, y no lo usa.
El algoritmo binario sobre con tablas de exponentes y logaritmos, en sus dos versiones: la única diferencia está en los dos coeficientes a y b de la actualización. Corre la Tabla 6 y después compara las dos versiones en los 575 patrones. Lo de los comentarios es la salida de correrlo.
from itertools import combinations
# GF(2^4) con 1 + X + X^4: EXP[i] es α^i como entero de 4 bits.
EXP, LOG = [1] * 30, {}
for i in range(1, 30):
x = EXP[i - 1] << 1
EXP[i] = x ^ 0b10011 if x & 0b10000 else x
for i in range(15):
LOG[EXP[i]] = i
def mul(a, b):
return 0 if a == 0 or b == 0 else EXP[LOG[a] + LOG[b]]
def inv(a):
return EXP[(15 - LOG[a]) % 15]
def alfa(x):
return '0' if x == 0 else '1' if x == 1 else 'α' if x == 2 else f'α^{LOG[x]}'
def sindromes(posiciones, t=3):
S = [0] * (2 * t + 1) # S[i] es S_i; S[0] no se usa
for i in range(1, 2 * t + 1):
for j in posiciones:
S[i] ^= EXP[(i * j) % 15]
return S
def berlekamp(S, t=3, sin_inversiones=False):
"""La versión binaria: filas (μ, σ, d, l), con μ = -1/2 como -0.5."""
filas = [(-0.5, [1], 1, 0)]
sigma, l = [1], 0
for mu in range(t + 1):
d = None
if mu < t: # d_μ = Σ σ_i S_{2μ+1-i}, con σ_0 incluido
d = 0
for i, c in enumerate(sigma[:2 * mu + 1]):
d ^= mul(c, S[2 * mu + 1 - i])
filas.append((mu, sigma, d, l))
if not d:
continue
rho = max((f for f in filas[:-1] if f[2]), key=lambda f: 2 * f[0] - f[3])
k = int(2 * (mu - rho[0])) # el corrimiento 2(μ - ρ)
if sin_inversiones:
a, b = rho[2], d # σ' = d_ρ σ + d_μ X^k σ^(ρ)
else:
a, b = 1, mul(d, inv(rho[2])) # σ' = σ + d_μ d_ρ⁻¹ X^k σ^(ρ)
nuevo = [mul(a, c) for c in sigma] + [0] * (len(rho[1]) + k)
for i, c in enumerate(rho[1]):
nuevo[i + k] ^= mul(b, c)
while nuevo[-1] == 0:
nuevo.pop()
sigma, l = nuevo, max(l, rho[3] + k)
return filas
S = sindromes([3, 5, 12])
print('S =', ', '.join(alfa(s) for s in S[1:]))
for nombre, si in (('con inversiones', False), ('sin inversiones', True)):
print(nombre)
for mu, sigma, d, l in berlekamp(S, sin_inversiones=si):
print(f' {mu:>4} σ = {" ".join(alfa(c) for c in sigma):<14} d = {alfa(d) if d is not None else "—":<4} l = {l}')
# Todos los patrones de 1, 2 y 3 errores: ¿el σ sin inversiones es c·σ?
distintos = 0
total = 0
for w in (1, 2, 3):
for pos in combinations(range(15), w):
S = sindromes(pos)
s1 = berlekamp(S)[-1][1]
s2 = berlekamp(S, sin_inversiones=True)[-1][1]
c = s2[0]
total += 1
distintos += [mul(x, inv(c)) for x in s2] != s1
print(total, 'patrones,', distintos, 'distintos')
# S = 1, 1, α^10, 1, α^10, α^5
# con inversiones
# -0.5 σ = 1 d = 1 l = 0
# 0 σ = 1 d = 1 l = 0
# 1 σ = 1 1 d = α^5 l = 1
# 2 σ = 1 1 α^5 d = α^10 l = 2
# 3 σ = 1 1 0 α^5 d = — l = 3
# sin inversiones
# -0.5 σ = 1 d = 1 l = 0
# 0 σ = 1 d = 1 l = 0
# 1 σ = 1 1 d = α^5 l = 1
# 2 σ = 1 1 α^5 d = α^10 l = 2
# 3 σ = α^5 α^5 0 α^10 d = — l = 3
# 575 patrones, 0 distintos
Los coeficientes van del grado 0 para arriba: 1 1 0 α^5 es . La discrepancia se calcula con adentro de la suma, que con inversiones vale 1 y sin inversiones no: así la misma línea sirve para las dos. Y la comparación del final divide por sólo para comparar; el decodificador no lo necesita.