伽罗华域GF(galois field)的乘法计算 - 查表法
在之前的文章中介绍了GF域的异或法乘法计算,但是现代计算机中,一个乘法运算使用异或计算的计算次数过多,速度太慢。而为了加速计算,常常使用的是空间换时间的方法,查表法应运而生。
伽罗华域(galois field)的乘法计算(异或法)
1. 基本原理
以GF(256)为例,GF域是一个由非0元素循环群且是一个有限域,在gf(256)每一个元素都不相等,
根据指数函数的单调递增的特性,我们可以将GF域表示为一个生成元α的指数形式:
GF(256)* = {1, α, α², ..., α²⁵⁴}
设两个非零元素:
a = αᶦ, b = αʲ (0 ≤ i,j ≤ 254)
则:
a·b = αᶦ·αʲ = αⁱ⁺ʲ
当 i+j < 255 时:
a·b = αⁱ⁺ʲ
当 i+j ≥ 255 时:
αⁱ⁺ʲ = αⁱ⁺ʲ mod 255 (因为 α²⁵⁵ = α⁰ = 1)
所以一般形式:
a·b = α⁽ⁱ⁺ʲ⁾ mod 255
这里有:
log[a] = log [αᶦ] = i, log[b] = log[αʲ ] = j
又有:
a·b = α⁽ⁱ⁺ʲ⁾ mod 255
= exp_a[i + j mod 255]
= exp_a[(log[a] + log[b]) mod 255]
由此我们可以得到公式:
a × b = exp_table[(log_table[a] + log_table[b]) mod 255]
这里的exp_table和log_table分别是生成元α的指数表和对数表
2. 表生成
2.1. GF(256) 的基本结构
- 不可约多项式: P(x) = x⁸ + x⁵ + x³ + x + 1 (0x12B)
- 乘法群阶: 255 (2⁸ - 1 = 255)
- 生成元 g: 阶为 255 的元素,满足 g²⁵⁵ = 1
2.2. 生成所需关系
- 指数表: g⁰, g¹, g², ..., g²⁵⁴ 的所有值
- 对数表: 每个非零元素到其指数的映射
指数表为所有生成元对应的指数值,有可能超过256,这时候则需要使用不可约多项式降幂,所以生成的指数表与两个元素有关系:
- 生成元
- 不可约多项式
对数生成则是将指数的值,转化为对数表的下标,将指数的下标转化对对数表的值去存放
2.3. 生成元的要求
生成元 g 需满足:
- g ≠ 0
- g 的阶为 255 (最小 k 使得 gᵏ ≡ 1 mod P(x) 的 k = 255)
2.4 有效生成元验证
在生成之前,还需要验证生成元是否是一个合适的生成元,使用费马小定理验证:
def is_generator(g, poly):# g^255 应该等于 1power = 1for _ in range(255):power = gf_mult(power, g, poly)return power == 1
2.5 详细代码实现
#include <vector>
using namespace std;const uint16_t POLY = 0x12B; // x⁸ + x⁵ + x³ + x + 1vector<uint8_t> generate_exp_table(uint8_t generator) {vector<uint8_t> exp_table(256, 0);uint8_t current = 1;exp_table[0] = 1; // g⁰ = 1// 生成 g¹ 到 g²⁵⁴for (int k = 1; k < 255; k++) {current = gf_mult_alpha(current, generator, POLY);exp_table[k] = current;}exp_table[255] = 1; // g²⁵⁵ ≡ g⁰ = 1return exp_table;
}// 计算 a × g mod POLY
uint8_t gf_mult_alpha(uint8_t a, uint8_t g, uint16_t poly) {uint8_t result = 0;// 标准乘法算法for (int i = 0; i < 8; i++) {if (g & 1) {result ^= a;}bool carry = (a & 0x80);a <<= 1;if (carry) {a ^= (poly & 0xFF);}g >>= 1;}return result;
}vector<uint8_t> generate_log_table(const vector<uint8_t>& exp_table) {vector<uint8_t> log_table(256, 0); // 初始化所有元素为0// 填充非零元素的对数for (int k = 0; k < 255; k++) {uint8_t value = exp_table[k];log_table[value] = k;}// 处理特殊值0 (可选)log_table[0] = 0xFF; // 使用特殊标记表示未定义return log_table;
}
2.6 验证方法
2.6.1 生成元幂次覆盖性
vector<bool> covered(256, false);
for (int k = 0; k < 255; k++) {covered[exp_table[k]] = true;
}
// 检查1-255是否全部覆盖
2.6.2 对数-指数互逆
for (int v = 1; v <= 255; v++) {if (exp_table[log_table[v]] != v) {// 错误处理}
}
2.6.3 幂运算一致性
// 验证 (g^k)^m = g^{k×m mod 255}
uint8_t test_exp = exp_table[k];
uint8_t power = gf_power(test_exp, m, POLY); // 独立幂计算
uint8_t expected = exp_table[(k * m) % 255];
assert(power == expected);
3. 计算
了解原理和表生成之后,计算就变得简单了
根据公式代入a和b的值:
a × b = exp_table[(log_table[a] + log_table[b]) mod 255]
P(x) = x⁸ + x⁴ + x³ + x² + 1 (十六进制 0x11D)
exp_table, log_table = generate_tables()a = 0x80
b = 0x32if a == 0 or b == 0:result = 0
else:log_a = log_table[a] # 7log_b = log_table[b] # 50log_sum = (log_a + log_b) % 255 # 57result = exp_table[log_sum] # 0x25