Imony Project
FlashBox
トップページImonyとは

PAR2の計算方法

分かりやすくプログラムを書いているところがないので資料程度に書いておきます。

はじめに

PAR2はn個のデータとm個のチェックサムを作り、それらのn+m個のブロックの内n個見つかったとすれば、復元できるという画期的な方法をさします。ここではプログラムに組み込むための資料ですので、証明は省かせていただきます。

この資料について

James S. Plankによる"A Tutorial on Reed-Solomon Coding for Fault-Torelance in RAID-like Systems"を参考に作成しました。また一部日本語訳も大いに参考になりましたのでこの場をかりて礼をさせていただきます。PAR2内部での計算方法を知るために有限体という項目を作りました。有限体をわかった上でPAR2の計算方法の項目へ進むことをオススメします。

有限体

有限体とは

代数学において、有限体(ゆうげんたい)とは、有限個の元からなる体、すなわち四則演算が定義され閉じている有限集合のことをさします。主に計算機関連の分野においては、ガロア体 (Galois field) とも呼びます。有限体の位数はある素数ρのべきであり、このときその標数はρとなります。四則演算が閉じていることにより、割り算の結果が分数になることや、掛け算の結果がとても大きな数値になること、はありません。プログラミングでは利便性の高い2のべきを使います。このことをGF(2^w)と書くこともあります。wに関しては1Byteが8Bitであるのでその約数・倍数である4、8、16が使われます。32に関しては大きくなりすぎているために現状ではほとんど使われていません。
参考: フリー百科事典『ウィキペディア(Wikipedia)』

有限体の計算

有限体の初期化

まずは定数の設定から始めます。PRIME_POLYは初期化のためによく考えられた数値と思ってください。
  1. // 8Bitの場合
  2. #define PAR2_BIT 8
  3. #define PAR2_PRIME_POLY 285
  4. #define PAR2_COUNT (1 << PAR2_BIT)
  5. #define PAR2_MAX (PAR2_COUNT - 1)
  6. // 4Bitの場合
  7. #define PAR2_BIT 4
  8. #define PAR2_PRIME_POLY 19
  9. #define PAR2_COUNT (1 << PAR2_BIT)
  10. #define PAR2_MAX (PAR2_COUNT - 1)
  11. // 16Bitの場合
  12. #define PAR2_BIT 16
  13. #define PAR2_PRIME_POLY 69643
  14. #define PAR2_COUNT (1 << PAR2_BIT)
  15. #define PAR2_MAX (PAR2_COUNT - 1)
以上を定義し、計算用の配列を作成します
  1. int gf[PAR2_COUNT], gfi[PAR2_COUNT];
  2. for (i = 0, j = 1; i < PAR2_MAX; i++) {
  3.     gf[j] = i; gfi[i] = j;
  4.     if (PAR2_MAX < (j <<= 1)) j ^= PAR2_PRIME_POLY;
  5. }

有限体の足し算引き算

どちらもプログラミング用語で言われている排他的論理和を使用します。BASIC系の言語であれば"XOR"で、C系の言語であれば"^"で計算することができます。
  1. int par2_add(int a, int b) {
  2.     return a ^ b;
  3. }

有限体の割り算掛け算

一旦、両方の対応する数を表より算出し、割り算は数を減らし、掛け算は足します。理屈がわからなくてもプログラムできますのであまり深く考えない方が良いかもしれません。
  1. // PAR2用掛け算
  2. int par2_mult(int a, int b) {
  3.     int c;
  4.     if (a == 0 || b == 0) return 0;
  5.     if ((c = gf[a] + gf[b]) < PAR2_MAX) return gfi[c];
  6.     return gfi[c - PAR2_MAX];
  7. }
  8. // PAR2用割り算(b=0は本来あってはならない)
  9. int par2_div(int a, int b) {
  10.     int c;
  11.     if (a == 0) return 0; if (b == 0) return -1;
  12.     if (0 <= (c = gf[a] - gf[b])) return gfi[c];
  13.     return gfi[c + PAR2_MAX];
  14. }

PAR2プログラミング

ここではImonyで使う8BITのPAR2で説明します。分かりやすくするために元データが4つにし、パリティを4つ生成します。元データは(D1 => 10, D2 => 200, D3 => 55, D4 => 11)とします。

パリティの生成

パリティ行列Pは行列F*データ行列Dによって求められます。行列Fはチェックサム行列が互いに素であるように作られる必要があり、また受け取り元でも同一の行列を生成できる必要があるので、生成方法はJames S. Plankが指定している方法に固定します。

行列F =
10203040
11213141
12223242
13233343
=
1111
1234
14516
181564

ここで3の二乗が5になっているのは計算が有機体で行われているからです。これよりパリティ行列Pは行列F*データ行列Dによって

1000
0100
0010
0001
1111
1234
14516
181564
D1
D2
D3
D4
=
D1
D2
D3
D4
P1
P2
P3
P4
で求まるので代入して
P =
1111
1234
14516
181564
10
200
55
11
=
254
242
86
222
と計算され(P1 => 254, P2 => 242, P3 => 86, P4 => 222)とパリティが求まります。

復元

この時、D1,D3,D4,P1を取得できなかった―(D2 => 200, P2 => 242, P3 => 86, P4 => 222)の4つが見つかったとします。つまりこれは行列で言うと、
0100
1234
14516
181564
D1
D2
D3
D4
=
D2
P2
P3
P4
これの逆行列をガウスの消去法で求めます。この時、データを別の所に置くと0で割ることになり機械的に計算することができなくなるので、データはもとの番号に置くことに注意してください。つまり
1234
0100
14516
181564
D1
D2
D3
D4
=
P2
D2
P3
P4
ということになります。この時の左の行列を変換行列Aと名づけます。変換行列Aは最初に正則行列になるように生成しているので必ず逆行列が存在します。ここで変換行列Aの逆行列を逆変換行列Xとします。ここではガウスの消去法と呼ばれる方法を使います。まず(A|E)を生成します。
12341000
01000100
145160010
1815640001
=
L1
L2
L3
L4
ここからL3,L4の一番左を消去する(0にする)ためにL1をそれぞれに足します。
12341000
01000100
066201010
01012681001
=
L1
L2
L3 + L1
L4 + L1
ここからL1,L3,L4の左から2番目を消去する(0にする)ためにL2を適度にかけてそれぞれに足します。
10341200
01000100
006201610
00126811001
=
L1 + 2 * L2
L2
L3 + 6 * L2
L4 + 10 * L2
L3の左から3番目が1になっていないので6で割ります。
10341200
01000100
001612211220
00126811001
=
L1
L2
L3 / 6
L4
ここからL1,L4の左から3番目を消去する(0にする)ためにL3を適度にかけてそれぞれに足します。
1001414311420
01000100
001612211220
0001083621
=
L1 + 3 * L3
L2
L3
L4 + 12 * L3
L4の左から4番目が1になっていないので108で割ります。
1001414311420
01000100
001612211220
0001961926432
=
L1
L2
L3
L4 / 108
ここからL1,L3の左から4番目を消去する(0にする)ためにL4を適度にかけてそれぞれに足します。
100024524541221
01000100
001039187231192
0001961926432
=
L1 + 14 * L4
L2
L3 + 6 * L4
L4
これで(E|逆行列X)の生成が完了です。よって元のデータを求めるにはこの逆行列に集めたD2,P2,P3,P4をかけます。
D =
24524541221
0100
39187231192
961926432
242
200
86
222
=
10
200
55
11
こうして元の行列を得る事ができました。