里德(Reed)-所罗门(Solomon)纠删码

纠删码是一种差错控制方式,常用在通信和存储中。

应用在存储中时,它和多副本相比,能用更少的空间实现与多副本相当水平的可靠性。

经典的纠删码两个参数,kmk 表示把数据分成多少部分,m 表示这 k 部分数据有 m 个校验信息。令 n=k+m,则 k 份数据占据 n 份空间,需要的额外空间比为 n/k1=m/k,而多副本——例如 r 副本——需要的额外空间比为 r1

纠删码的用途和优劣势

优势:省空间

在实际生产中,多副本的 r 一般取 3,需要额外 2 倍空间,纠删码 (k,m) 一般取 (6,3) 之类的值,需要额外 0.5 倍空间。

劣势:性能差,计算负担重

所以纠删码适用的场景是,读很少,写极少。典型的例子是陈年卷宗。

环上的代数与纠删码原理

原理

d1,d2,d3​ 是需要保存的数据,考虑如下矩阵

A=(100010001111124)

A(d1d2d3)=(d1d2d3c1c2)

显然,c1=d1+d2+d3,而 c2=d1+2d2+4d3​,可以证明 A 去掉任何两行后,都会得到一个满秩矩阵 B,在上式右边去掉行号相同的对应两行,得到

(t1t2t3)

那么

B1(t1t2t3)=(d1d2d3)

于是,我们在丢失了两个数据后,通过代数运算,还原了原始数据。这就是 (k,m)=(3,2) 的里德所罗门编码的基本原理。它可以容忍任意 m 个数据丢失。

但是,上述计算直接应用在计算机上,则完全行不通,因为计算机只能处理有限数,不能处理实数。所以,必须通过某种转换,使得计算机也能处理这样的计算。

有限域

不难发现,上面的计算只用到了加减乘除(除数不能是零),矩阵的求逆也是有限次加减乘除的复合,没有用到实数的连续性等其他性质。所以我们可以(也必须)用一个对计算机友好的、支持加减乘除的代数结构替换上面的实数集 R

域,Field,指的是一个非空集合 F,上面有加法和乘法两个运算,且:

  • 加法使 F 成为交换群 F+,其单位元记为 0
  • 乘法满足交换律,且使 F{0} 成为交换群 F× ,其单位元记为 1
  • 满足乘法对加法的分配律,a(b+c)=ab+ac

如果 F 是有限集,那么就称这个域是有限域,也叫伽罗瓦域。

可以证明,有限域的元素个数必须是 pk,其中 p 是素数,k 是正整数。

一个字节有 256 个不同的值,即 0 到 255,而幸运的是, 256 可以作为有限域的元素个数,因为它是 28。所以我们的目标是,构造一个元素个数为 256 的域 F,它和

B={0,1,,255}

之间建立起一一对应

h:BF

于是 B 中的四则运算可以定义为

a+b=h1(h(a)+h(b))

ab=h1(h(a)h(b))

ab=h1(h(a)h(b))

a/b=h1(h(a)/h(b))

有限域的构造

构造元素个数为 256 的域需要一些小波折。但好在,构造元素个数为素数的域很简单。

p 为素数。考虑整数的模 p 同余类 Z/pZ,加法就是相加后取模 p 同余,乘法就是相乘后取模 p 同余,相反数定义为如果 p 整除 a+b 则称 ab 互为相反数,a 的倒数定义为使 ab1(modp) 的那个 b,有了相反数和倒数就可以定义减法和除法。根据初等数论知识,这些定义都是良定义的,并且满足域的条件,它是有限域 Fp

特别地,p=2,那么 F={0,1},加减法都是亦或,乘法是只有 1×1=1 其他乘积都等于零。这是有 2 个元素的有限域 F2

正如我们用整数环和素数 p 构造有限域 Fp​ 那样,可以考虑系数在 F2​ 中的多项式组成的多项式环 F2[x] 以及该环上的本原多项式构造有限域。

考察多项式

p(x)=x8+x4+x3+x2+1

它在 F2 中是本原多项式。在 F2 中取模 p(x) 同余,得到由次数小于 8 的多项式环

GF=GF(256)={f(x)|f(x)=i=07aixi,aiF2}

它是 F2 上的 8 维线性空间,里面正好有 28=256 个向量。

GF 中的加减法按照多项式自然的加减法进行,加法单位元为零多项式 0,乘法按照多项式相乘模 p(x),除零多项式外,乘法都有逆元,乘法的单位元是零次多项式 1,于是,它满足域的所有条件。

终于,我们得到了一个有 256 个元素的域!

代码实现

在开始代码之前,我们还是要继续啰嗦几句代数。

一个有限域 F,它的乘法群 F× 必定是循环群,也就是群 F× 的所有元素都可以由某个不是单位元的元素不断乘以自己得到。具体到 GF(256),它里面的 1 次多项式 x 可以生成它里面除零多项式之外的所有多项式

GF(256)={0,1,x,x2,...,x254}

x 的阶 ord(x)=255,这就为我们编码实现提供了便利。

给定多项式 f(x)=xng(x)=xm,计算它们的乘积可以直接把指数加起来 f(x)g(x)=xnxm=xn+m。而在代码中,我们可以直接构造两个表:

  • 指数表:给定 n,得到 xn 展开后的样子,byte 表示
  • 对数表:给定 byte,它对应的多项式是 x 的多少次幂

有了这两个表,我们计算乘除法才好算。

constexpr int FIELD_SIZE = 256;

constexpr uint16 PRIMITIVE_POLYNOMIAL = 0b100011101; // x^8 + x^4 + x^3 + x^2 + 1

uint8 gf_power[FIELD_SIZE - 1];
uint8 gf_log[FIELD_SIZE];

// 计算指数表和对数表
// 在实际生产中,提前在别的地方算好放入 constexpr 数组,这里为了演示无所谓了
void init()
{
    constexpr uint16 MASK = 0xFF00;

    gf_power[0] = uint8(1);
    gf_power[1] = uint8(1) << 1;
    for (int i = 2; i < FIELD_SIZE - 1; ++i)
    {
        uint16 temp = gf_power[i - 1] << 1;
        if ((temp & MASK) != 0)
        {
            // 越界了,取模
            temp ^= PRIMITIVE_POLYNOMIAL;
        }
        gf_power[i] = static_cast<uint8>(temp);
    }

    gf_log[0] = 0; // 注意,0 没有对数
    for (int i = 0; i < FIELD_SIZE - 1; ++i)
    {
        gf_log[gf_power[i]] = i;
    }
}

除此之外,还要补充矩阵相关的实现,例如逆运算、行列式、余子阵、余子式、代数余子式之类的。

完整代码参考我的仓库:

https://github.com/jthmath/study-reed-solomon-ec

posted @   风华神使  阅读(1342)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示