`ZZ_N` 上的函数空间 对固定的正整数 `N`, 设 `f(k)` 是定义在 `ZZ_N = 0, 1, 2, cdots, N-1` 上的复值函数. 这个函数也可以写成向量形式: `f = (f_0, f_1, cdots, f_(N-1))`. 它的生成函数定义为一个多项式 `F(x) = sum_(0 le k lt N) f_k x^k`. `ZZ_n` 上两个函数 (即向量) 的内积定义为 `(f, g) = sum_(0 le r lt N) f(r) bar(g(r))`.
正交基底 把单位圆周平均分为 `N` 份, 第 `j` 个分点记为 `omega^j = "e"^(2pi"i"j//N)`, 取 `ZZ_N` 上的一组函数 `epsi_j = (1, omega^j, cdots, omega^((N-1)j))`, `quad j in ZZ_N`, 则它们两两正交: `(epsi_j, epsi_k) = { N, if j = k; 0, otherwise :}` 且 `epsi_0, epsi_1, cdots, epsi_(N-1)` 构成 `ZZ_N` 上的函数空间的一组正交基底.
当 `j = k` 时 `(epsi_j, epsi_k) = sum_(0 le r lt N) omega^(j r) omega^(-j r) = N`.
`j != k` 时, 记 `omega^(j-k) = q`, 有 `q != 1` 和 `q^N = 1`. 于是
`(epsi_j, epsi_k) = sum_(0 le r lt N) omega^(j r) omega^(-k r)`
`= sum_(0 le r lt N) q^r`
`= (1-q^N)/(1-q) = 0`,
最后, 任意 `f = (f_0, f_1, cdots, f_(N-1))` 都能被 `epsi_0, cdots, epsi_(N-1)` 线性表示,
这是因为假设 `f = sum_(r=0)^(N-1) k_r epsi_r`, 则方程的系数行列式为 Vandermonde 行列式, 必有唯一解.
离散 Fourier 变换 `f` 在 `ZZ_N` 上的 Fourier 系数定义为 `a_N^j(f) ==^"简记" a_j` `:= 1/N (f, epsi_j) = 1/N sum_(0 le k lt N) f_k omega^(-j k)`. 向量 `(a_0, a_1, cdots, a_(N-1))` 称为 `f` 的 Fourier 变换, 记为 `a = hat f`. 如果已知 `a`, 要求 `f`, 只需对 `a_j` 施行 Fourier 逆变换: `f_k = sum_(0 le j lt N) a_j omega^(j k)`.
由于 `{epsi_j//sqrt N}_(j=0)^(N-1)` 构成 `ZZ_N` 上函数空间的标准正交基, 有 `f = sum_(0 le j lt N) (f, epsi_j/sqrt N) epsi_j/sqrt N`, 取第 `k` 个分量得 `f_k = sum_(0 le j lt N) 1/N (f, epsi_j) epsi_j(k)` `= sum_(0 le j lt N) a_j omega^(j k)`.
从生成函数的角度看, `f_k` 等于多项式 `A(x) = sum_(0 le j lt N) a_j x^j` 在 `x = omega^k` 处的值, 而 `a_j` 等于多项式 `F(x) = sum_(0 le k lt N) f_k x^k` 在 `x = omega^-j` 处的值除以 `N`.
已知 `hat f = (4, 3, 2, 1)`, 求 `f`.
由 Fourier 逆变换公式, `f_k = 4 * 1^k + 3 * "i"^k + 2 * (-1)^k + 1 * (-"i")^k`, 代入 `k = 0, 1, 2, 3` 得 `f = (10, 2+2"i", 2, 2-2"i")`.
递推公式
设 `N = 2^n`,
若 `f` 是 `ZZ_(2N)` 上的函数, 则 `g(r) := f(2r)`, `h(r)
:= f(2r+1)` 是 `ZZ_N` 上的函数. 有如下递推公式
`a_(2N)^j(f) = (a_N^j(g) + a_N^j(h) * omega_(2N)^-j) // 2`,
`a_(2N)^(N+j)(f) = (a_N^j(g) - a_N^j(h) * omega_(2N)^-j) // 2`.
这里 `omega_N^j = "e"^(2pi"i"j//N)`.
对于逆变换, 也有完全类似的公式, 只需把上式 `f, a` 的地位对调,
再用 `omega` 的共轭代替 `omega`.
由
`a_(2N)^j(f)`
`= 1/(2N) sum_(0 le r lt N)
(f(2r) omega_(2N)^(-2r j) + f(2r+1) omega_(2N)^(-(2r+1)j))`
`= 1/(2N) sum_(0 le r lt N)
(g(r) omega_N^(-r j) + h(r) omega_N^(-r j) * omega_(2N)^-j)`
即得结论.
从生成函数的角度看, `F(x)` 是 `2N-1` 次多项式,
`G(x) := f_0 + f_2 x + cdots + f_(2N-2) x^(N-1)`,
`H(x) := f_1 + f_3 x + cdots + f_(2N-1) x^(N-1)`
是两个 `N-1` 次多项式, 满足关系 `F(x) = G(x^2) + x H(x^2)`.
代入 `x = omega_(2N)^-j` 有
`F(omega_(2N)^-j) = G(omega_(2N)^(-2j)) + omega_(2N)^-j H(omega_(2N)^(-2j))`
`= G(omega_N^-j) + omega_(2N)^-j H(omega_N^-j)`,
上式两边同除以 `2N` 就得到结论.
此外还有
`F(omega_(2N)^(-N-j)) = F(-omega_(2N)^-j)`
`= G(omega_N^-j) - omega_(2N)^-j H(omega_N^-j)`.
快速 Fourier 变换 (FFT) 由递推公式知道, 欲求 `ZZ_(2N)` 上的函数 `f` 的 Fourier 变换, 可以将它拆成两个 `ZZ_N` 上的函数 `g, h`, 分别求得 Fourier 变换后, 再合并得到 `hat f`. 由于 `hat f` 共有 `2N` 个分量, 所以合并操作的时间复杂度为 `O(N)`. 设 FFT 的时间复杂度为 `T(N)`, 则 `T(2N) = 2 T(N) + O(N)`, 解得 `T(N) = O(N log N)`.
CRC 校验 (Cyclic Redundancy Check, 循环冗余校验) 是用于通信的校验算法. 该算法的输入是一个二进制串 `b` 和 一个 `bbb F_2` 上的 `n` 次既约多项式 `f`, 输出是一个次数小于 `n` 的多项式.
111 --- 商 x^2 + x + 1 不重要, 可以不写 --------- 1 0011 / 111 1111 100 11 ------- 11 001 10 011 ------- 1 0101 1 0011 ------ 110 --- 余数 x^2 + x
说明: mul2 表示 `bbb F_2` 上的乘法, mod2 表示 `bbb F_2` 上的取余.
例3 表明, 若 mod2(x, y) = r, 则 mod2(x + r, y) = 0. 这里的 + 代表异或.
例4 的结果与下文的 CRC-16 的例2 结果相同.
例5 和例6 展示了分步求余的技巧:
310000 31320000 % 11021 % 11021 ------- -------- 2672 20b5 + 3200 --------- 147200 % 11021 --------- 20b5 310000 = 11021 * q + 2672 31000000 = 11021 * q * 100 + 267200 31320000 = 11021 * q * 100 + 267200 + 320000 故 mod2(31320000, 11021) 等于 mod2(267200 + 320000, 11021)
数字 `n` 称为 CRC 算法的位宽 (width), 比如 CRC-8 的位宽等于 8, CRC-32 的位宽等于 32 等. 多项式 `f` 称为 CRC 算法的生成多项式. 如 CRC-8 的生成多项式是 `x^8 + x^2 + x + 1`, CRC-32 的生成多项式是 `x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1`. `f` 用二进制表示时, 由于首项系数必为 1, 只记录 `n` 个低次项即可. 如 CRC-8 和 CRC-32 的生成多项式分别记为 0x07 和 0x04C1 1DB7
下面这个计算 CRC-16 的例子来自 CSDN, 其中运用了分步求余的技巧.
例2、例3 表明 bytes 可以分成多段计算, 把上一次的结果作为 init 传入下一次的计算中.
例4 表明把 CRC 附在数据后面一起计算时, 结果为 0.
这里是 CRC 在线计算.
Hamming 码 规定数据的第 `2^i` 位 (`i = 1, 2, 4, 8, cdots`) 作为校验位, 其余位置作为数据位. 当数据传输时出现 1bit 的错误时, Hamming 码可以发现并修复错误. 当校验位本身出错时, Hamming 码不能正确修复错误. 以下是一个例子:
data: 1001 1010 // 待传输的数据, 低位在前: 0x59 encode: __1_ 001_ 1010 // 1 所在的位置是 3 7 9 11 parity: 3^7^9^11 = 0110 // 校验位 hamming: 0111 0010 1010 // 从右向左填充校验位 received: 0111 0010 1110 // 假设第 10 位在传输中出错 parity': 3^7^9^10^11 = 1100 // 接收方计算的校验位 parity xor: 1100 ^ 0110 = 1010 // 即十进制的 10, 表明第 10 位在传输中出错