enFGK FGK符号 deFGK FGK符号からの復号
FGK符号
long enFGK(unsigned char *code, long clen,
unsigned char *text, long tlen);
FGK符号からの復号
long deFGK(unsigned char *text, long tlen,
unsigned char *code, long clen);
FGK符号関数において
code (入出力)FGK符号を格納する配列
clen (入力) 配列codeの長さ
text (入力) テキストを格納する配列
tlen (入力) 配列textの長さ
FGK符号からの復号関数において
text (入出力)テキストを格納する配列
tlen (入力) 配列textの長さ
code (入力) FGK符号を格納する配列
clen (入力) 配列codeの長さ
FGK符号関数について
FGK符号の長さ。符号化に失敗したときは -1L。
FGK符号からの復号関数について
テキストの長さ。復号に失敗したときは -1L。
typedef struct {
int chr; /* 文字 */
long count; /* 子ノードの出現頻度の和 */
int parent; /* 親ノードへ */
int left; /* 左側の子ノードへ */
int right; /* 右側の子ノードへ */
} NODE;
#define ROOT 0 /* ルート */
#define EOF_NODE 1 /* EOFノード */
#define CHAR_SIZE 256
#define NODE_SIZE (2*CHAR_SIZE+2) /* ノードの最大個数 */
long enFGK(unsigned char *code, long clen,
unsigned char *text, long tlen)
{
int i, j, k;
long c, len;
int curChar;
int zeroNode, thisNode, maxid;
int node2id[NODE_SIZE]; /* ノードからそのノード番号 */
int char2node[CHAR_SIZE];
NODE node[NODE_SIZE]; int freeNode;
char bit[NODE_SIZE]; char *bitp;
static unsigned char bit0[8] = {~128,~64,~32,~16,~8,~4,~2,~1};
static unsigned char bit1[8] = { 128, 64, 32, 16, 8, 4, 2, 1};
if (tlen <= 0) return -1L;
for (i = 0; i < CHAR_SIZE; i++) char2node[i] = 0;
/* 初期ハフマン木 */
zeroNode = 2;
node[2].count = 0;
node[2].parent = ROOT;
node[EOF_NODE].count = 1;
node[EOF_NODE].parent = ROOT;
node[ROOT].count = 0x7FFFFFFFL;
node[ROOT].parent = 0;
node[ROOT].left = zeroNode;
node[ROOT].right = EOF_NODE;
node2id[zeroNode] = 0;
node2id[EOF_NODE] = 1;
node2id[ROOT] = 2;
freeNode = 3;
/* 各文字を符号化 */
for (i = 0, c = 0, len = 0, bitp = bit; ; c++) {
/* 入力文字に対応するノード */
if (c >= tlen) thisNode = EOF_NODE;
else if ((thisNode = char2node[curChar = text[c]]) == 0) {
thisNode = zeroNode;
for (j = 7; j >= 0; j--) *bitp++ = curChar & bit1[j];
}
/* 符号語を得る */
k = thisNode;
do {
int parent = node[k].parent;
*bitp++ = (node[parent].left == k) ? 0 : 1;
k = parent;
} while (k != ROOT);
/* 逆順に出力 */
do {
if (*--bitp) *code |= bit1[i];
else *code &= bit0[i];
if (++i == 8) {
code++;
if (++len > clen) return -1L;
i = 0;
}
} while (bitp != bit);
if (c == tlen) break; /* EOF */
/* ハフマン木を更新 */
while (thisNode != ROOT) {
if (thisNode == zeroNode) {
for (j = 0; j < freeNode; j++) node2id[j] += 2;
node[zeroNode].left = freeNode;
node[freeNode].count = 0;
node[freeNode].parent = zeroNode;
node2id[freeNode] = 0;
freeNode++;
node[zeroNode].right = freeNode;
node[freeNode].count = 1;
node[freeNode].parent = zeroNode;
node2id[freeNode] = 1;
char2node[curChar] = freeNode; /* 新文字 */
freeNode++;
thisNode = zeroNode;
zeroNode = freeNode - 2;
} else {
if (node[thisNode].count == 1 &&
node[k = node[thisNode].parent].count == 1) {
/* 0-ノードの兄弟 */
for (j = 0, maxid = thisNode; j < freeNode; j++)
if (node[j].count == 1 && j != k &&
node2id[j] > node2id[maxid]) maxid = j;
} else {
for (j = 0, maxid = thisNode; j < freeNode; j++)
if (node[thisNode].count == node[j].count &&
node2id[j] > node2id[maxid]) maxid = j;
}
if (maxid != thisNode) {
int maxidP = node[maxid].parent;
int thisP = node[thisNode].parent;
int id = node2id[maxid];
if (node[maxidP].left == maxid)
node[maxidP].left = thisNode;
else node[maxidP].right = thisNode;
if (node[thisP].left == thisNode)
node[thisP].left = maxid;
else node[thisP].right = maxid;
node[maxid].parent = thisP;
node[thisNode].parent = maxidP;
node2id[maxid] = node2id[thisNode];
node2id[thisNode] = id;
}
node[thisNode].count++;
thisNode = node[thisNode].parent;
}
}
}
if (i > 0) len++;
return (len > clen) ? -1L : len;
}
long deFGK(unsigned char *text, long tlen,
unsigned char *code, long clen)
{
int i, j, k;
long c, len;
int curChar;
int zeroNode, thisNode, maxid;
int node2id[NODE_SIZE]; /* ノードからそのノード番号 */
NODE node[NODE_SIZE]; int freeNode;
static unsigned char bit1[8] = { 128, 64, 32, 16, 8, 4, 2, 1};
if (clen <= 0) return -1L;
/* 初期ハフマン木 */
zeroNode = 2;
node[2].chr = CHAR_SIZE+1;
node[2].count = 0;
node[2].parent = ROOT;
node[EOF_NODE].chr = CHAR_SIZE;
node[EOF_NODE].count = 1;
node[EOF_NODE].parent = ROOT;
node[ROOT].count = 0x7FFFFFFFL;
node[ROOT].parent = 0;
node[ROOT].left = zeroNode;
node[ROOT].right = EOF_NODE;
node2id[zeroNode] = 0;
node2id[EOF_NODE] = 1;
node2id[ROOT] = 2;
freeNode = 3;
/* 復号化 */
for (i = 0, c = 0, len = 0; ; ) {
/* 符号語に対応する文字ノードを得る */
thisNode = ROOT;
do {
thisNode = (*code & bit1[i]) ?
node[thisNode].right : node[thisNode].left;
if (thisNode >= freeNode) return -1L;
if (++i == 8) {
code++;
if (++c > clen) return -1L;
i = 0;
}
} while (node[thisNode].chr < 0);
if (thisNode == EOF_NODE) break; /* EOF */
if (thisNode == zeroNode) { /* 新文字 */
curChar = 0;
for (j = 0; j < 8; j++) {
if (*code & bit1[i]) curChar |= bit1[j];
if (++i == 8) {
code++;
if (++c > clen) return -1L;
i = 0;
}
}
} else curChar = node[thisNode].chr;
if (++len > tlen) return -1L;
*text++ = curChar; /* 文字の出力 */
/* ハフマン木を更新 */
while (thisNode != ROOT) {
if (thisNode == zeroNode) {
for (j = 0; j < freeNode; j++) node2id[j] += 2;
node[zeroNode].left = freeNode;
node[freeNode].count = 0;
node[freeNode].parent = zeroNode;
node2id[freeNode] = 0;
node[freeNode].chr = CHAR_SIZE + 1;
freeNode++;
node[zeroNode].right = freeNode;
node[freeNode].count = 1;
node[freeNode].parent = zeroNode;
node2id[freeNode] = 1;
node[freeNode].chr = curChar;
freeNode++;
thisNode = zeroNode;
node[thisNode].chr = -1;
zeroNode = freeNode - 2;
} else {
if (node[thisNode].count == 1 &&
node[k = node[thisNode].parent].count == 1) {
/* 0-ノードの兄弟 */
for (j = 0, maxid = thisNode; j < freeNode; j++)
if (node[j].count == 1 && j != k &&
node2id[j] > node2id[maxid]) maxid = j;
} else {
for (j = 0, maxid = thisNode; j < freeNode; j++)
if (node[thisNode].count == node[j].count &&
node2id[j] > node2id[maxid]) maxid = j;
}
if (maxid != thisNode) {
int maxidP = node[maxid].parent;
int thisP = node[thisNode].parent;
int id = node2id[maxid];
if (node[maxidP].left == maxid)
node[maxidP].left = thisNode;
else node[maxidP].right = thisNode;
if (node[thisP].left == thisNode)
node[thisP].left = maxid;
else node[thisP].right = maxid;
node[maxid].parent = thisP;
node[thisNode].parent = maxidP;
node2id[maxid] = node2id[thisNode];
node2id[thisNode] = id;
}
node[thisNode].count++;
thisNode = node[thisNode].parent;
}
}
}
return len;
}
これらの問題点を解決するために、入力テキストから1文字を読み込むごとに、 その時点までの各文字の出現頻度を再計算し、ハフマン木をつくり直し、文字を 符号化することが考案された。ハフマン木はその時点での出現頻度に対して最適 であり続けるように変化するので、適応型ハフマン符号という。またハフマン木 が文字ごとに変わるので、動的ハフマン符号ともいう。
しかし、実際にこのようにしてハフマン木を文字ごとに再構成することは、かな りの計算時間がかかる。実際の適応型ハフマン符号アルゴリズムでは、ハフマン 木の変形を最小限にするための工夫が必要である。
適応型ハフマン符号を少ない計算量で実現するアイデアは、最初 1970 年代に N. Faller と R. G. Gallager によって独立に考案された。1985年に Knuth が このアイデアに改良を加え、FGKアルゴリズムを開発した。
アルゴリズムの基礎は、Gallager によって定義された兄弟条件である。ある 木が兄弟条件を満たすとは、ルート以外の各ノードが1つの兄弟をも ち、しかも、ノードを兄弟同士が隣り合うように重みの非増加順に並べること ができる場合をいう。Gallager は、ある木がハフマン木であるのは、その木が 兄弟条件を満たす場合であり、かつその場合に限ることを示した。ここでいう 重みとはノードの文字出現頻度ののことで、内部ノードの文字出現頻度はその 子ノードのそれの和である。
FGK符号においても、初期ハフマン木はルートとその子ノード2つからなる。 2つの子ノードは、それぞれ「未出現文字(以下 0-ノードと呼ぶ)」とEOF (符号の終了を表す特別記号)に対応する。0-ノードの出現頻度を 0、EOF ノードの出現頻度を 1 とする。また、ノード番号は 0-ノードが 0、EOFノード が 1、ルートが 2 の順とする。
0-ノードは、現在までに出現していない文字が初めて入力テキスト中に現れた ときに、その文字を符号化するために使う。その場合、0-ノードに対する符号 語を出力した後に、文字そのものも出力する(たとえば ASCIIコード等による)。
ハフマン木を以下のように更新する。