rules=none>
|
6.11.97
|
|
name=L3>
Описание метода Динамического сжатия Маркова.
|
name=L4>
Захаров Максим
|
name=L5>
|
30.03.1993
|
|
|
name=L7>
Данное документ составлен на основе первоначального описания алгоритма,
|
name=L8>
опубликованного в [1]. Кратко описывается алгоритм и приводятся результаты
|
name=L9>
сравнения этого алгоритма с другими методами сжатия. Более подробно о работе
|
name=L10>
алгоритма, в частности, о тонких местах в выборе параметров и совместном
|
name=L11>
использовании GUAZZO CODING ALGORITHM, можно прочитать в [1]. В работе [2]
|
name=L12>
показывается, что модель Динамического сжатия Маркова эквивалентна автомату с
|
name=L13>
конечным контекстом (Finite Context Automata, FCA). Т.к. класс FCA является
|
name=L14>
подмножеством класса автоматов с конечным числом состояний (Finite State
|
name=L15>
Automata, FSA), то делается предположение, что т.к. фрагменты текстов на
|
name=L16>
английском языке могут бытьгуаспознаны FSA, но не могут распознаваться
|
name=L17>
Марковской моделью переменного порядка (variable-order Markov model), то
|
name=L18>
использование модели FSA потенциально должно давать лучшие результаты сжатия.
|
name=L19>
|
name=L20>
1. G.V. Cormack, R.N. Horspool.
|
name=L21>
Data Compression Using Dynamic Markov Modelling.
|
name=L22>
// The Computer Journal, vol.30,No.6,1987, pp.541-550
|
name=L23>
2. T. Bell, A. Moffat.
|
name=L24>
A note on the DMC data compression scheme.
|
name=L25>
// The Computer Journal, vol.32,No.1,1989, pp.16-20
|
name=L26>
|
name=L27>
------------------------
|
name=L28>
|
name=L29>
На практике, сообщения почти всегда имеют некоторую степень зависимости
|
name=L30>
между соседними символами, что соответствует источнику сообщений с памятью. В
|
name=L31>
частности, таким источником может быть Марковская модель (в каждом состоянии
|
name=L32>
она помнит только об одном предыдущем своем состоянии).
|
name=L33>
Для примера используем простую Марковскую модель. Будем считать, что
|
name=L34>
источник порождает строки, состоящие из двоичных цифр такие, что:
|
name=L35>
за 0 следует 0 с вероятностью 2/3
|
name=L36>
за 0 следует 1 с вероятностью 1/3
|
name=L37>
за 1 следует 0 с вероятностью 2/5
|
name=L38>
за 1 следует 1 с вероятностью 3/5
|
name=L39>
и первая цифра может быть 0 или 1 равновероятно (1/2).
|
name=L40>
|
name=L41>
Сообщения, порождаемые этой моделью имеют общее свойство: за 0 следует чаще
|
name=L42>
другой 0, чем 1. Аналогично - за 1 чаще следует 1, чем 0. Марковская модель
|
name=L43>
может быть описана с помощью ориентированного графа, каждой дуге которого
|
name=L44>
приписываются соответствующие вероятности перехода. Диаграмма состояний для
|
name=L45>
этой модели выглядит следующим образом:
|
name=L46>
---
|
name=L47>
| А |
|
name=L48>
/ ---
|
/
|
name=L49>
0(50%) / 1(50%)
|
name=L50>
/
|
|/ / 0 (40%) |
|
name=L51>
--- --------- --- /
|
name=L52>
--- | B | | C | ----
|
name=L53>
| --- --------- --- |
|
name=L54>
0 (67%)| | 1 (33%) / | |1 (60%)
|
name=L55>
------ --------
|
name=L56>
|
name=L57>
При автоматическом создании такой модели возникает две проблемы: проблема
|
name=L58>
определения вероятностей перехода, приписываемых дугам графа, и проблема в
|
name=L59>
определении самой структуры графа. Эти проблемы рассматриваются по
|
name=L60>
отдельности.
|
name=L61>
|
name=L62>
|
Определение вероятностей перехода
|
|
|
name=L64>
Пусть уже известна структура графа. По мере прочитывания двоичных цифр
|
name=L65>
сообщения Марковская модель будет переходить из одного своего состояния в
|
name=L66>
другое в соответствии с прочитанными символами. Необходимо подсчитать сколько
|
name=L67>
раз модель будет переходить по каждой дуге (для каждой дуги заводятся счетчики
|
name=L68>
переходов по этой дуге). С помощью этих счетчиков можно получить оценки
|
name=L69>
вероятностей переходов. Если переход из состояния А по дуге для символа 0
|
name=L70>
проходил N0 раз, а по дуге для 1 - N1, то оценки вероятностей будут выглядеть
|
name=L71>
следующим образом:
|
name=L72>
Prob{digit=0 | current state=A} = N0/(N0 + N1)
|
name=L73>
Prob{digit=1 | current state=A} = N1/(N0 + N1)
|
name=L74>
Чем чаще мы будем попадать в состояние А, тем точнее будут эти оценки.
|
name=L75>
При адаптивном кодировании заранее определить значения N0 и N1 невозможно.
|
name=L76>
В этом случае эти оценки могут выглядеть следующим образом:
|
name=L77>
Prob{digit=0 | current state=A} = (N0 + c)/(N0 + N1 + 2c)
|
name=L78>
Prob{digit=1 | current state=A} = (N1 + c)/(N0 + N1 + 2c)
|
name=L79>
где с - положительная константа. Для файлов большого размера (на много больше
|
name=L80>
значения c) выбор конкретного значения c существенной роли не играет. Для
|
name=L81>
файлов малого размера при малом значении c модель быстрее 'выучит'
|
name=L82>
характеристики источника, однако, при этом увеличивается вероятность
|
name=L83>
неправильного прогноза. (Т.е. типичная проблема адаптивных алгоритмов).
|
name=L84>
|
name=L85>
|
Построение диаграммы состояний
|
|
|
name=L87>
Алгоритм динамического построения диаграммы состояний поясняется на простом
|
name=L88>
примере: пусть уже построена модель, содержащая состояния A,B,...,E:
|
name=L89>
|
name=L90>
--- 0 --- 0 ---
| A |-------- | C |-------- | D |
--- / --- ---
/|
/
1 / 1
/
/
--- / | ---
| B | /------------------ | E |
--- ---
|
name=L91>
name=L92>
name=L93>
name=L94>
name=L95>
name=L96>
name=L97>
name=L98>
name=L99>
|
name=L100>
Из диаграммы видно, что в состояние C ведут две дуги из A и B и выходит две
|
name=L101>
дуги: в D и E. Всякий раз, когда модель переходит в состояние C, теряется
|
name=L102>
некоторая информация о контексте, т.е. забывается откуда мы пришли - из A или
|
name=L103>
B. Однако вполне может быть, что выбор следующего состояния (D или E) зависит
|
name=L104>
от предыдущего состояния (A или B). Чтобы учесть эту зависимость прибегнем к
|
name=L105>
следующему приему: разделим состояние C на два состояния. Измененная модель
|
name=L106>
будет иметь вид:
|
name=L107>
|
name=L108>
--- 0 --- 0 ---
|
name=L109>
| A |-------- | C |-------- | D |
|
name=L110>
--- --- 1 /| ---
|
name=L111>
/
|
name=L112>
/
|
name=L113>
/
|
name=L114>
0 /
|
/
|
name=L115>
--- 1 --- / ---
|
name=L116>
| B |-------- | С'|------- | E |
|
name=L117>
--- --- 1 / ---
|
name=L118>
|
name=L119>
Значения счетчиков переходов из C в D или E делится между счетчиками C и C'
|
name=L120>
в новой модели пропорционально числу переходов из A в C и из B в C для старой
|
name=L121>
модели. Теперь модель отражает степень зависимости между состояниями A,B и
|
name=L122>
D,E. Для этой модели возможно выбор следующего состояния (D или E) не зависит
|
name=L123>
от предыдущего состояния (A или B), но зависит от непосредственного
|
name=L124>
предшественника A или B. В этом случае делим состояния A,B,C' и еще раз делим
|
name=L125>
состояние C. Т.е. модель теперь будет учитывать эти зависимости.
|
name=L126>
В основном, чем больше мы делим состояний, тем больше увеличивает размах
|
name=L127>
зависимостей, используемых моделью для прогноза. Само собой процесс деления
|
name=L128>
необходимо производить только в том случае, когда мы уверены в существовании
|
name=L129>
соответствующих зависимостей, иначе мы потеряем информацию о контексте.
|
name=L130>
Отсюда желательно, чтобы деление состояния C проходило тогда, когда оба
|
name=L131>
счетчика переходов AC и BC достаточно велики. На основе этого можно вывести
|
name=L132>
критерий деления состояния:
|
name=L133>
Выбранное состояние делится в том и только в том случае, если число переходов
|
name=L134>
из текущего состояния в выбранное состояние больше, чем MIN_CNT1, и число
|
name=L135>
переходов из всех других состояний, кроме текущего, в выбранное состояние
|
name=L136>
больше, чем MIN_CNT2.
|
name=L137>
|
name=L138>
|
Начальная модель
|
|
|
name=L140>
Для полного определения метода динамического сжатия Маркова осталось
|
name=L141>
определить стартовую модель. Самая простая модель выглядит следующим образом:
|
name=L142>
|
name=L143>
-----------------
|
name=L144>
|/ |
|
name=L145>
--- |
|
name=L146>
----------| |---------------
|
name=L147>
| ---
|
| |
|
name=L148>
-------------
|
name=L149>
Самая простая модель для байт-ориентированного варианта имеет 255 состояний,
|
name=L150>
представляется в виде двоичного дерева у которого переход из каждого листа
|
name=L151>
ведет в корень дерева.
|
name=L152>
В целом, древовидная начальная модель работает хорошо, но можно построить
|
name=L153>
модель, которая будет работать немного лучше. В общем случае, состояния на
|
name=L154>
k-м уровне дерева учитывает зависимость от предыдущих k-1 бит. Размер
|
name=L155>
контекста, от которого зависит текущее состояние увеличивается от 0 до 7 бит,
|
name=L156>
затем, при переходе к корню дерева, эта зависимость теряется. Лучшей по
|
name=L157>
сравнению с этой будет модель, сохраняющая постоянным размер контекста. Т.е.
|
name=L158>
такая модель должна учитывать зависимость между последними битами предыдущего
|
name=L159>
байта и первыми битами текущего бита. К сожалению, такую модель трудно
|
name=L160>
представить в виде диаграммы, но алгоритм построения этой модели приводится в
|
name=L161>
конце данной статьи.
|
name=L162>
|
name=L163>
|
name=L164>
|
Последний важный момент
|
|
|
name=L166>
Этим последним моментом является вопрос когда прекращать производить
|
name=L167>
деление состояния модели. Если этого не производить, то через некоторое время
|
name=L168>
модель может занять всю имеющуюся память и потребовать еще. Одно из возможных
|
name=L169>
решений - определить предел числа состояний модели по достижении которого
|
name=L170>
модель разрушается и начинает создаваться заново из начальной модели.
|
name=L171>
Последствия такой операции можно облегчить, если запомнить перед разрушением
|
name=L172>
модели предыдущие n байт текста и затем по этому фрагменту улучшить начальную
|
name=L173>
модель перед продолжением обработки текста.
|
name=L174>
|
name=L175>
Зависимость коэффициента сжатия от параметров деления
|
name=L176>
(MIN_CNT1, MIN_CNT2)
|
name=L177>
-----------+---------------------+-----------------------
|
name=L178>
Значение | Число вершин графа | Коэффициент сжатия
|
name=L179>
параметров | | (%)
|
name=L180>
-----------+---------------------+-----------------------
|
name=L181>
( 1,1 )* | > 194000 | 34.7
|
name=L182>
( 2,2 ) | 150901 | 33.8
|
name=L183>
( 4,4 ) | 84090 | 35.8
|
name=L184>
( 8,8 ) | 44296 | 38.9
|
name=L185>
(16,16) | 23889 | 42.7
|
name=L186>
(32,32) | 12089 | 46.5
|
name=L187>
(64,64) | 6347 | 50.6
|
name=L188>
(128,128) | 3211 | 54.6
|
name=L189>
(256,256) | 1711 | 58.6
|
name=L190>
-----------+---------------------+-----------------------
|
name=L191>
Файл, на котором проводились исследования, содержал 97393
|
name=L192>
символов ASCII текста.
|
name=L193>
* Сжав 90% исходного файла, программа исчерпала ресурс
|
name=L194>
памяти для узлов графа и была прервана.
|
name=L195>
|
name=L196>
Сравнительные результаты работы
|
name=L197>
------------+-------------------------------------------------+
|
name=L198>
| Исследуемые файлы |
|
name=L199>
+--------------+----------------+---------+-------+
|
name=L200>
Метод |Форматированый|Неформатированый|Объектный|С-текст|
|
name=L201>
сжатия | текст | текст | код | |
|
name=L202>
------------+--------------+----------------+---------+-------+
|
name=L203>
Adaptive | 59.7 | 61.6 | 79.6 | 62.9 |
|
name=L204>
Huffman | | | | |
|
name=L205>
------------+--------------+----------------+---------+-------+
|
name=L206>
Normal | | | | |
|
name=L207>
Ziv-Lempel | 38.2 | 42.9 | 98.4 | 40.8 |
|
name=L208>
(LZW) | | | | |
|
name=L209>
------------+--------------+----------------+---------+-------+
|
name=L210>
Bit-oriented| 74.2 | 83.6 | 91.3 | 86.7 |
|
name=L211>
(LZ-2) | | | | |
|
name=L212>
------------+--------------+----------------+---------+-------+
|
name=L213>
Cleary and | | | | |
|
name=L214>
Witten | 26.5 | 30.2 | 69.4 | 26.3 |
|
name=L215>
(CW) | | | | |
|
name=L216>
------------+--------------+----------------+---------+-------+
|
name=L217>
Dynamic | | | | |
|
name=L218>
Markov | 27.2 | 31.8 | 54.8 | 27.5 |
|
name=L219>
(DMC) | | | | |
|
name=L220>
------------+--------------+----------------+---------+-------+
|
name=L221>
File size | 74510 | 61536 | 68608 | 31479 |
|
name=L222>
------------+--------------+----------------+---------+-------+
|
name=L223>
|
name=L224>
|
Алгоритм деления состояния модели
|
|
|
name=L226>
{ NEXT_STATE[S,D] = state reached from S after trsnsition on digit D
|
name=L227>
TRANS_CNT[S,D] = number of observations of input D when in state S
|
name=L228>
STATE = number of current state
|
name=L229>
LAST_STATE = largest state number used so far
|
name=L230>
MIN_CNT1 = minimum number of transitions from the current
|
name=L231>
state to state S before S is eligible for cloning
|
name=L232>
MIN_CNT2 = minimum number of visits to a state S from all
|
name=L233>
predecessors of S other than the current state
|
name=L234>
before S is eligible for cloning }
|
name=L235>
while not eof do
|
name=L236>
begin
|
name=L237>
resd( B ); { read one input bit }
|
name=L238>
TRANS_CNT[STATE,B] := TRANS_CNT[STATE,B] + 1;
|
name=L239>
NXT := NEXT_STATE[STATE,B];
|
name=L240>
NXT_CNT := TRANS_CNT[NXT,0] + TRANS_CNT[NXT,1];
|
name=L241>
if (TRANS_CNT[STATE,B] > MIN_CNT1) and
|
name=L242>
((TRANS_CNT - TRANS_CNT[STATE,B]) > MIN_CNT2) then
|
name=L243>
begin
|
name=L244>
LAST_STATE := LAST_STATE + 1;
|
name=L245>
NEW := LAST_STATE; { Obtain new state number }
|
name=L246>
NEXT_STATE[STATE,B] := NEW;
|
name=L247>
RATIO := TRANS_CNT[STATE,B] / NXT_CNT;
|
name=L248>
for B:=0 to 1 do
|
name=L249>
begin
|
name=L250>
NEXT_STATE[NEW,B] := NEXT_STATE[NXT,B];
|
name=L251>
TRANS_CNT[NEW,B] := RATIO * TRANS_CNT[NXT,B];
|
name=L252>
TRABS_CNT[NXT,B] := TRANS_CNT[NXT,B] - TRANS_CNT[NEW,B]
|
name=L253>
end;
|
name=L254>
NXT := NEW
|
name=L255>
end;
|
name=L256>
STATE := NXT
|
name=L257>
end;
|
name=L258>
|
name=L259>
|
Алгоритм генерации начального состояния
|
|
|
name=L261>
const
|
name=L262>
NBITS = 8; { Number of bits per byte }
|
name=L263>
STRANDS = 256; { 2 ** NBITS }
|
name=L264>
|
name=L265>
for I := 0 to NBITS-1 do
|
name=L266>
for J :=0 to STRANDS-1 do
|
name=L267>
begin
|
name=L268>
STATE := I + NBITS * J;
|
name=L269>
K := (I + 1) mod NBITS;
|
name=L270>
NEXT_STATE[STATE,0] := K + ((2 * j) mod STRANDS) * NBITS;
|
name=L271>
NEXT_STATE[STATE,1] := K + ((2 * j + 1) mod STRANDS) * NBITS;
|
name=L272>
TRANS_CNT[STATE,0] := 1;
|
name=L273>
TRANS_CNT[STATE,0] := 1
|
name=L274>
end;
|
name=L275>
LAST_STATE := NBITS * STRANDS -1;
|
name=L276>
|
name=L277>
|
name=L278>
|
Учебный вариант алгоритма Динамического сжатия Маркова
|
|
|
name=L280>
Ниже приводится реализация алгоритма Динамического сжатия Маркова,
|
|
написанный автором алгоритма для учебных целей. Этот алгоритм уже публиковался
|
name=L282>
в этой конференции и приводится здесь для полноты.
|
|
name=L283>
|
name=L284>
/* Dynamic Markov Compression (DMC) Version 0.0.0
|
name=L285>
|
name=L286>
Copyright 1993, 1987
|
name=L287>
Gordon V. Cormack
|
name=L288>
University of Waterloo
|
name=L289>
cormack@uwaterloo.ca
|
name=L290>
|
name=L291>
|
name=L292>
All rights reserved.
|
name=L293>
|
name=L294>
This code and the algorithms herein are the property of Gordon V. Cormack.
|
name=L295>
|
name=L296>
Neither the code nor any algorithm herein may be included in any software,
|
name=L297>
device, or process which is sold, exchanged for profit, or for which a
|
name=L298>
licence or royalty fee is charged.
|
name=L299>
|
name=L300>
Permission is granted to use this code for educational, research, or
|
name=L301>
commercial purposes, provided this notice is included, and provided this
|
name=L302>
code is not used as described in the above paragraph.
|
name=L303>
|
name=L304>
*/
|
name=L305>
/* This program implements DMC as described in
|
name=L306>
"Data Compression using Dynamic Markov Modelling",
|
name=L307>
by Gordon Cormack and Nigel Horspool
|
name=L308>
in Computer Journal 30:6 (December 1987)
|
name=L309>
|
name=L310>
It uses floating point so it isn't fast. Converting to fixed point
|
name=L311>
isn't too difficult.
|
name=L312>
|
name=L313>
comp() and exp() implement Guazzo's version of arithmetic coding.
|
name=L314>
|
name=L315>
pinit(), predict(), and pupdate() are the DMC predictor.
|
name=L316>
|
name=L317>
pflush() reinitializes the DMC table and reclaims space
|
name=L318>
|
name=L319>
preset() starts the DMC predictor at its start state, but doesn't
|
name=L320>
reinitialize the tables. This is used for packetized
|
name=L321>
communications, but not here.
|
name=L322>
*/
|
name=L323>
#include <stdio.h>
|
name=L324>
|
name=L325>
float predict();
|
name=L326>
int pinit();
|
name=L327>
int pupdate();
|
name=L328>
|
name=L329>
int memsize = 0x1000000;
|
name=L330>
|
name=L331>
main(argc,argv)
|
name=L332>
int argc;
|
name=L333>
char *argv[];
|
name=L334>
{
|
name=L335>
if (argc == 3 && isdigit(*argv[2])) sscanf(argv[2],"%d",&memsize);
|
name=L336>
if (argc >= 2 && *argv[1] == 'c') comp();
|
name=L337>
else if (argc >= 2 && *argv[1] == 'e') exp();
|
name=L338>
else {
|
name=L339>
fprintf(stderr,"usage: dmc [ce] memsize <infile >outfilen");
|
name=L340>
exit(1);
|
name=L341>
}
|
name=L342>
return 0;
|
name=L343>
}
|
name=L344>
|
name=L345>
comp(){
|
name=L346>
int max = 0x1000000,
|
name=L347>
min = 0,
|
name=L348>
mid,
|
name=L349>
c,i,
|
name=L350>
inbytes = 0,
|
name=L351>
outbytes =3,
|
name=L352>
pout = 3,
|
name=L353>
bit;
|
name=L354>
|
name=L355>
pinit(memsize);
|
name=L356>
|
name=L357>
for(;;){
|
name=L358>
c = getchar();
|
name=L359>
if (c == EOF) {
|
name=L360>
min = max-1;
|
name=L361>
fprintf(stderr,"compress done: bytes in %d, bytes out %d, ratio %fn",
|
name=L362>
inbytes,outbytes,(float)outbytes/inbytes);
|
name=L363>
break;
|
name=L364>
}
|
name=L365>
for (i=0;i<8;i++){
|
name=L366>
bit = (c << i) & 0x80;
|
name=L367>
mid = min + (max-min-1) * predict();
|
name=L368>
pupdate(bit != 0);
|
name=L369>
if (mid == min) mid++;
|
name=L370>
if (mid == (max-1)) mid--;
|
name=L371>
|
name=L372>
if (bit) {
|
name=L373>
min = mid;
|
name=L374>
} else {
|
name=L375>
max = mid;
|
name=L376>
}
|
name=L377>
while ((max-min) < 256) {
|
name=L378>
if(bit)max--;
|
name=L379>
putchar(min >> 16);
|
name=L380>
outbytes++;
|
name=L381>
min = (min << 8) & 0xffff00;
|
name=L382>
max = ((max << 8) & 0xffff00 ) ;
|
name=L383>
if (min >= max) max = 0x1000000;
|
name=L384>
}
|
name=L385>
}
|
name=L386>
if(!(++inbytes & 0xff)){
|
name=L387>
if(!(inbytes & 0xffff)){
|
name=L388>
fprintf(stderr,
|
name=L389>
"compressing... bytes in %d, bytes out %d, ratio %fr",
|
name=L390>
inbytes,outbytes,(float)outbytes/inbytes);
|
name=L391>
}
|
name=L392>
if (outbytes - pout > 256) { /* compression failing */
|
name=L393>
pflush();
|
name=L394>
}
|
name=L395>
pout = outbytes;
|
name=L396>
}
|
name=L397>
}
|
name=L398>
putchar(min>>16);
|
name=L399>
putchar((min>>8) & 0xff);
|
name=L400>
putchar(min & 0x00ff);
|
name=L401>
}
|
name=L402>
|
name=L403>
|
name=L404>
exp(){
|
name=L405>
int max = 0x1000000,
|
name=L406>
min = 0,
|
name=L407>
mid,
|
name=L408>
val,
|
name=L409>
i,
|
name=L410>
inbytes=3,
|
name=L411>
pin=3,
|
name=L412>
outbytes=0,
|
name=L413>
bit,
|
name=L414>
c;
|
name=L415>
|
name=L416>
pinit(memsize);
|
name=L417>
|
name=L418>
val = getchar()<<16;
|
name=L419>
val += getchar()<<8;
|
name=L420>
val += getchar();
|
name=L421>
while(1) {
|
name=L422>
c = 0;
|
name=L423>
if (val == (max-1)) {
|
name=L424>
fprintf(stderr,"expand: input %d output %dn",inbytes,outbytes);
|
name=L425>
break;
|
name=L426>
}
|
name=L427>
for (i=0;i<8;i++){
|
name=L428>
mid = min + (max-min-1)*predict();
|
name=L429>
if (mid == min) mid++;
|
name=L430>
if (mid == (max-1)) mid--;
|
name=L431>
if (val >= mid) {
|
name=L432>
bit = 1;
|
name=L433>
min = mid;
|
name=L434>
} else {
|
name=L435>
bit = 0;
|
name=L436>
max = mid;
|
name=L437>
}
|
name=L438>
pupdate(bit != 0);
|
name=L439>
c = c + c + bit;
|
name=L440>
while ((max-min) < 256) {
|
name=L441>
if(bit)max--;
|
name=L442>
inbytes++;
|
name=L443>
val = (val << 8) & 0xffff00 | (getchar()& 0xff);
|
name=L444>
min = (min << 8) & 0xffff00;
|
name=L445>
max = ((max << 8) & 0xffff00 ) ;
|
name=L446>
if (min >= max) max = 0x1000000;
|
name=L447>
}
|
name=L448>
}
|
name=L449>
putchar(c);
|
name=L450>
if(!(++outbytes & 0xff)){
|
name=L451>
if (inbytes - pin > 256) { /* compression was failing */
|
name=L452>
pflush();
|
name=L453>
}
|
name=L454>
pin = inbytes;
|
name=L455>
}
|
name=L456>
}
|
name=L457>
}
|
name=L458>
|
name=L459>
typedef struct nnn {
|
name=L460>
float count[2];
|
name=L461>
struct nnn *next[2];
|
name=L462>
} node;
|
name=L463>
|
name=L464>
static int threshold = 2, bigthresh = 2;
|
name=L465>
|
name=L466>
static node *p, *new, nodes[256][256];
|
name=L467>
|
name=L468>
static node *nodebuf;
|
name=L469>
static node *nodemaxp;
|
name=L470>
static node *nodesptr;
|
name=L471>
|
name=L472>
#include <malloc.h>
|
name=L473>
|
name=L474>
pinit(memsize)
|
name=L475>
int memsize;
|
name=L476>
{
|
name=L477>
fprintf(stderr,"using %d bytes of predictor memoryn",memsize);
|
name=L478>
nodebuf = (node *) malloc (memsize);
|
name=L479>
if (nodebuf == (node *) NULL) {
|
name=L480>
fprintf(stderr,"memory alloc failed; try smaller predictor memoryn");
|
name=L481>
exit(1);
|
name=L482>
}
|
name=L483>
nodemaxp = nodebuf + (memsize/sizeof(node)) - 20;
|
name=L484>
pflush();
|
name=L485>
}
|
name=L486>
|
name=L487>
pflush(){
|
name=L488>
int i,j;
|
name=L489>
for (j=0;j<256;j++){
|
name=L490>
for (i=0;i<127;i++) {
|
name=L491>
nodes[j][i].count[0] = 0.2;
|
name=L492>
nodes[j][i].count[1] = 0.2;
|
name=L493>
nodes[j][i].next[0] = &nodes[j][2*i + 1];
|
name=L494>
nodes[j][i].next[1] = &nodes[j][2*i + 2];
|
name=L495>
}
|
name=L496>
for (i=127;i<255;i++) {
|
name=L497>
nodes[j][i].count[0] = 0.2;
|
name=L498>
nodes[j][i].count[1] = 0.2;
|
name=L499>
nodes[j][i].next[0] = &nodes[i+1][0];
|
name=L500>
nodes[j][i].next[1] = &nodes[i-127][0];
|
name=L501>
}
|
name=L502>
}
|
name=L503>
nodesptr = nodebuf;
|
name=L504>
preset();
|
name=L505>
}
|
name=L506>
|
name=L507>
preset(){
|
name=L508>
p = &nodes[0][0];
|
name=L509>
}
|
name=L510>
|
name=L511>
float predict(){
|
name=L512>
return p->count[0] / (p->count[0] + p->count[1]);
|
name=L513>
}
|
name=L514>
|
name=L515>
pupdate(b)
|
name=L516>
int b;
|
name=L517>
{
|
name=L518>
float r;
|
name=L519>
if (p->count[b] >= threshold &&
|
name=L520>
p->next[b]->count[0]+p->next[b]->count[1]
|
name=L521>
>= bigthresh + p->count[b]){
|
name=L522>
new = nodesptr++;
|
name=L523>
p->next[b]->count[0] -= new->count[0] =
|
name=L524>
p->next[b]->count[0] *
|
name=L525>
(r = p->count[b]/(p->next[b]->count[1]+p->next[b]->count[0]));
|
name=L526>
p->next[b]->count[1] -= new->count[1] =
|
name=L527>
p->next[b]->count[1] * r;
|
name=L528>
new->next[0] = p->next[b]->next[0];
|
name=L529>
new->next[1] = p->next[b]->next[1];
|
name=L530>
p->next[b] = new;
|
name=L531>
}
|
name=L532>
p->count[b]++;
|
name=L533>
p = p->next[b];
|
name=L534>
if (nodesptr > nodemaxp){
|
name=L535>
fprintf(stderr,"flushing ...n");
|
name=L536>
pflush();
|
name=L537>
}
|
name=L538>
}
|
name=L539>
|