序文 §

LFSRの勉強をしていた際に、Berlekamp-Messeyのアルゴリズムが出てきたのでそれ関連でリードソロモン符号について調べてみたら、結構凄さを感じたアルゴリズムだったので紹介します。

問題設定 §

次のような符号化を考える(最初に考案されたリードソロモン符号)。

  1. メッセージ長をmm、符号長をnnとする。なお、許容される符号の誤りはnm12\frac{n - m - 1}2個までとなる
  2. 各バイトやパケットを有限体Fp\mathbb F_pの要素に変換して、それを係数とするFp\mathbb F_p上のm1m-1次多項式f(x)f(x)を作る
  3. 長さnnの符号を次のように生成する
    1. ci=f(ai)c_i = f(a_i)
    2. ここでaia_iiji \neq jなら互いに異なるFp\mathbb F_pの要素であり、送信者と受信者で共有されている

この符号C=(f(a1),f(a2),,f(an))C = (f(a_1), f(a_2), \dots, f(a_n))に対して誤りが前述のnm12\frac{n-m-1}2個であればそれを訂正出来るアルゴリズム(Berlekamp-Welch Algorithm)が存在する1。以降、この数をkkとおく(knm12k\coloneqq \frac{n-m-1}2)

アルゴリズムの大まかな流れはq(x)=e(x)f(x)q(x) = e(x)f(x)となるような多項式q(x),e(x)q(x), e(x)を求め、q(x)e(x)\frac{q(x)}{e(x)}を計算してf(x)f(x)を求めるという形になる。

アルゴリズム §

  • input: 受信した(kk以下の誤りを含む)符号 (y1,y2,,yn)(y_1, y_2, \dots, y_n)
  • output: f(x)f(x)

アルゴリズム本体の前に前節の最後で触れたg(x),e(x)g(x), e(x)に対し、e(x)e(x)が何であるかについて示す。これはyif(ai)y_i \neq f(a_i)であるようなyiy_iを解に持つ多項式であり、次のような形になる。

E{aiyif(ai)}e(x)iE(xai) \begin{aligned} E &\coloneqq \{a_i \mid y_i \neq f(a_i)\} \cr e(x) &\coloneqq \prod_{i \in E} (x - a_i) \end{aligned}

このe(x)e(x)を用いると任意のiiに対して次が成り立つ。

e(ai)f(ai)=e(ai)yi e(a_i)f(a_i) = e(a_i)y_i

誤りがない、つまりyi=f(ai)y_i = f(a_i)の時に成り立つのは自明であり、そうでない場合もe(ai)=0e(a_i) = 0となることからこの等式が成り立つ。ここで多項式q(x)e(x)f(x)q(x) \coloneqq e(x)f(x)を定義すれば、当然q(ai)e(ai)yi=0q(a_i) - e(a_i)y_i = 0が任意のiiで成り立つ。

ここで、q(x)q(x)e(x)e(x)は未知であるが、その係数を変数とした連立方程式を考えるとこれは線形方程式となる。e(x)e(x)はその構成からkk次のモニック方程式であるので、既知の係数(1)である先頭を除いて、未知の係数は高々kk個となる。また、q(x)q(x)の次数は定義からk+mk+m次となるので未知の係数はk+m+1k+m+1個ある。よって全体で2k+m+12k+m+1個の未知変数が存在する線形方程式を解くので、これより多くのaia_iが必要であり、したがって、n2k+m+1n \geq 2k+m+1である。

e(x),q(x)e(x), q(x)について次のように定義する。

e(x)j=0kejxjq(x)j=0k+mqjxj \begin{aligned} e(x) \coloneqq \sum_{j=0}^k e_jx^j\cr q(x) \coloneqq \sum_{j=0}^{k+m} q_jx^j \end{aligned}

これを用いると任意のiiに対して次が成り立つ。

q0+q1ai+q2ai2++qk+maik+myi(e0+e1ai+e2ai2++ek1aik1+aik)=0 q_0 + q_1a_i + q_2a_i^2 + \dots + q_{k+m}a_i^{k+m} - y_i(e_0 + e_1a_i + e_2a_i^2 + \dots + e_{k-1}a_i^{k-1} + a_i^k) = 0

eekk次の係数は1であることから、この式にはyiaik-y_ia_i^kという既知の項が存在する。よってこれを右辺に持っていくことで次の等式が成り立つ。

q0+q1ai+q2ai2++qk+maik+myi(e0+e1ai+e2ai2++ek1aik1)=yiaik q_0 + q_1a_i + q_2a_i^2 + \dots + q_{k+m}a_i^{k+m} - y_i(e_0 + e_1a_i + e_2a_i^2 + \dots + e_{k-1}a_i^{k-1}) = y_ia_i^k

未知数であるej,qje_j, q_jは線形の項しか存在しないので、全てのiiに対するこの等式は次のような行列で表すことが出来る。

(1a1a12a1k+my1y1a1y1a12y1a1k11a2a22a2k+my2y2a2y2a22y2a2k11anan2ank+mynynanynan2ynank1)(q0q1q2qk+me0e1e2ek1)=(y1a1ky2a2kynank) \begin{pmatrix} 1 & a_1 & a_1^2 &\dots& a_1^{k+m} & -y_1 & -y_1a_1 & -y_1a_1^2 & \dots & y_1a_1^{k-1} \cr 1 & a_2 & a_2^2 &\dots& a_2^{k+m} & -y_2 & -y_2a_2 & -y_2a_2^2 & \dots & y_2a_2^{k-1} \cr &&&&&\vdots \cr 1 & a_n & a_n^2 &\dots& a_n^{k+m} & -y_n & -y_na_n & -y_na_n^2 & \dots & y_na_n^{k-1} \cr \end{pmatrix} \begin{pmatrix} q_0 \cr q_1 \cr q_2 \cr \vdots \cr q_{k+m} \cr e_0 \cr e_1 \cr e_2 \cr \vdots \cr e_{k-1} \end{pmatrix} = \begin{pmatrix} y_1a_1^k \cr y_2 a_2^k \cr \vdots \cr y_na_n^k \end{pmatrix}

これが解ければ、e(x)e(x)q(x)q(x)が手に入るのでq(x)q(x)e(x)e(x)で割って、f(x)f(x)が手に入る。

実装(SageMath) §

K = GF(521)
PR.<x> = PolynomialRing(K)

m = 5
coeffs = [K.random_element() for _ in range(m)]
f = PR(coeffs)
k = 3
n = 2*k+m+1

A = [i+1 for i in range(n)]
code = [f(a) for a in A]

for _ in range(k):
    i = randint(0, 11)
    code[i] = K.random_element()

M = []
v = []
for i in range(n):
    row_left = [K(A[i])^e for e in range(k+m+1)]
    row_right = [-code[i] * K(A[i])^e for e in range(k)]
    row = row_left + row_right

    v.append(code[i]*K(A[i])^k)

    M.append(row)

M = matrix(K, M)
v = vector(K, v)
polys = M.solve_right(v)

q = PR(list(polys[:k+m+1]))
e = PR(list(polys[k+m+1:]) + [1])

assert q / e == f

References §


1

ちなみに誤りが無いならラグランジュ補間でf(x)f(x)を復元出来る