АЛГОРИТМЫ СЖАТИЯ

ТеорияПрактикаКонтроль знанийДемонстрация
Содержание
Введение
Классификация
-Критерии оценки
-Надёжность и сложность
-Методы сжатия
-Методы кодирования
Сжатие без потерь
-RLE
-Семейство LZ
-LZ77
-LZSS
-LZ78
-LZW
-LZM
-LZB
-LZH
-LZC
-LZT
-LZMV
-LZJ
-LZFG
-Унарное кодирование
-Метод Хаффмана
-Арифметическое кодирование
-Вероятностное сжатие
-BWT
Сжатие с потерями
-Звук и видео
-Изображения
Алгоритмы сжатия с потерями
-JPEG
-JPEG2000
-Wavelet
-Фрактальный
Предметный указатель

Burrows-Wheeler Transform

Исходный код реализации алгоритма BWT (Burrows-Wheeler Transform) - скачать (Visual C++).

Алгоритм BWT - теория.

Листинги исходного кода реализации алгоритма BWT

main.cpp
#include <iostream>
#include <algorithm>
#include <windows.h>
#include "perf_tim.h"
#include <deque>

template <unsigned ALIGN>
inline unsigned alignDown(unsigned value) {
 return value&(0ul-ALIGN);
}

template <unsigned ALIGN>
inline unsigned alignUp(unsigned value) {
 return value+ALIGN-1ul&(0ul-ALIGN);
}

inline unsigned getBitString(const unsigned char *bits,
                                            unsigned num) {
 return (bits[num/8]>>(7ul-num%8))&1ul;
}

template <unsigned BIT>
inline unsigned maskBit() {
 return (1ul<<BIT)-1ul;
}

#define MASK_BIT(bit) ((1ul<<(bit))-1ul)

//--------------------------------------------------------
// класс описывающий узел
__declspec(align(4)) class Node
{
public:
 // get
 // links
 Node *getXlink(unsigned id) const
 {
  return reinterpret_cast<Node*>(linktag_[id]&~1ul);
 }
 Node *getLlink() const
 {
  return getXlink(0);
 }
 Node *getRlink() const
 {
  return getXlink(1);
 }
 // tags
 unsigned getXtag(unsigned id) const
 {
  return linktag_[id]&1ul;
 }
 unsigned getLtag() const
 {
  return getXtag(0);
 }
 unsigned getRtag() const
 {
  return getXtag(1);
 }
 // skip
 unsigned getSkip() const
 {
  return skip_;
 }
 // set
 // links&tags
 void setXlinktag(unsigned id,Node *link,unsigned tag)
 {
  linktag_[id]=reinterpret_cast<unsigned>(link)|tag;
 }
 void setLlinktag(Node *llink,unsigned tag)
 {
  setXlinktag(0,llink,tag);
 }
 void setRlinktag(Node *rlink,unsigned tag)
 {
  setXlinktag(1,rlink,tag);
 }
 // skip
 void setSkip(unsigned skip)
 {
  skip_=skip;
 }
 // init root
 void initializeRoot()
 {
  // левый линк указывает на себя (корень)
  setLlinktag(this,1);
 }
private:
 unsigned linktag_[2];
 unsigned skip_;
};
//--------------------------------------------------------
// класс алгоритма P (см. Кнут, "Искусство программирования",
// "Сортировка и поиск")
class PatAlgorithm
{
public:
    // инициализирующий конструктор
 PatAlgorithm(Node *root)
  : q_(root)
  , p_(q_->getLlink())
  , j_(p_->getSkip())
  , qid_(0)
  , qtag_(q_->getLtag())
 {
    }
    // инициализация существуюшего объекта
 void initialize(Node *root)
 {
  q_=root; p_=q_->getLlink();
  j_=p_->getSkip();
        qid_=0; qtag_=q_->getLtag();
 }
    // прогон алгоритма P
    void run(const unsigned char *k,unsigned n)
 {
  if (qtag_==0) while (j_<n&&iterateByK(k));
    }
 // найти контекст по строке k числом символов n
 // начало текстового буфера - text
 bool findContext(Node *root,const unsigned char *k,
  unsigned n,const unsigned char *text)
 {
     run(k,n*8);
        return std::equal(k,k+n,text+getText(root));
    }
 unsigned getText(Node *root) const
 {
  return p_-root;
 }
protected:
 // одна классическая итерация алгоритма P
    bool iterate()
 {
  q_=p_;
  p_=q_->getXlink(qid_);
  qtag_=q_->getXtag(qid_);
  if (qtag_) return false;
  j_=p_->getSkip();
        return true;
    }
    // итерация по ключу
    bool iterateByK(const unsigned char *k)
 {
     qid_=getBitString(k,j_);
        return iterate();
    }
 Node *q_,*p_;
    unsigned j_;
    unsigned qid_,qtag_;
};
//--------------------------------------------------------
// класс прохода по дереву
class PatIterator
: public PatAlgorithm
{
public:
    // инициализирующий конструктор
 PatIterator(Node *root)
  : PatAlgorithm(root)
  , lowBound_(0)
 {
    }
 PatIterator(const PatIterator &pit)
  : PatAlgorithm(pit)
  , lowBound_(descent_.size())
 {
 }
 // прогон алгоритма P влево (ограничение по битам)
 void runLeft(unsigned n)
 {
  if (qtag_==0&&j_<n)
  {
   qid_=0;
   do
   {
    descent_.push_back(p_);
   } while (iterate()&&j_<n);
  }
 }
 // прогон алгоритма P влево (без ограничений)
 void runLeft()
 {
  if (qtag_==0)
  {
   qid_=0;
   do
   {
    descent_.push_back(p_);
   } while (iterate());
  }
 }
    // идти направо
    bool goRight()
 {
  if (descent_.size()>lowBound_)
  {
   p_=descent_.back();
   descent_.pop_back();
   j_=p_->getSkip();
   qid_=1;
   iterate();
   return true;
  }
  else
   return false;
    }
    unsigned getJ(unsigned w) const
 {
     return qtag_?w*8:j_+1;
    }
protected:
    // прогон алгоритма P с историей узлов Q
    void run(const unsigned char *k,unsigned n)
 {
  if (qtag_==0) while (j_<n)
  {
         descent_.push_back(p_);
   if (!iterateByK(k))
    break;
  }
    }
 static std::deque<Node*> descent_;
 const unsigned lowBound_;
};
//--------------------------------------------------------
std::deque<Node*> PatIterator::descent_;
//--------------------------------------------------------
// функция посимвольного сравнения строк
inline unsigned compareStringByte(const unsigned char *a,
          const unsigned char *b)
{
 const unsigned char *startA=a;
 for (;*a==*b;++a,++b);
 return a-startA;
}
//--------------------------------------------------------
// сравнение строк
inline unsigned compareString(const unsigned char *a,
         const unsigned char *b)
{
 const unsigned
  offA=reinterpret_cast<unsigned>(a)%4,
  offB=reinterpret_cast<unsigned>(b)%4;
 const unsigned
  *alA=reinterpret_cast<const unsigned*>
    (alignDown<4>(reinterpret_cast<unsigned>(a))),
  *alB=reinterpret_cast<const unsigned*>
    (alignDown<4>(reinterpret_cast<unsigned>(b)));
 unsigned id=0;
 // если фазы совпадают
 if (offA==offB)
 {
  if (offA)
  {
   for (;offA+id!=4;++id)
   {
    if (*a++!=*b++)
     return id;
   }
   ++alA; ++alB;
  }
  for (;*alA++==*alB++;id+=4);
  a+=alignDown<4>(id); b+=alignDown<4>(id);
  for (;*a++==*b++;++id);
 }
 else
  for (a+=id,b+=id;*a++==*b++;++id);
 return id;
}
//--------------------------------------------------------
class PatAddNodeBase
: protected PatAlgorithm
{
public:
 static void initAdd()
 {
  lastId_=lastMatch_=0;
 }
protected:
 PatAddNodeBase();
 PatAddNodeBase(PatAddNodeBase&);
 PatAddNodeBase(Node *root)
  : PatAlgorithm(root)
 {
 }
 unsigned matchFunc(unsigned id,const unsigned char *k,
  const unsigned char *key)
 {
  unsigned offs=id-lastId_;
  lastId_=id;
  return lastMatch_=
   lastMatch_>offs
   ?offs=lastMatch_-offs,offs
    +compareStringByte(k+offs,key+offs)
   :compareStringByte(k,key);
 }
 static unsigned lastId_,lastMatch_;
};
//--------------------------------------------------------
unsigned PatAddNodeBase::lastId_=0;
unsigned PatAddNodeBase::lastMatch_=0;
//--------------------------------------------------------
// класс добавления нового узла в дерево
// (с проверкой на конец)
class PatAddNode
: private PatAddNodeBase
{
public:
    // конструктор, выполняющий добавление немедленно
 PatAddNode(unsigned L,Node *root,
  const unsigned char *text,unsigned id)
  : PatAddNodeBase(root)
 {
  const unsigned char *k=text+id;
  run(k,L*8);
  const unsigned char *key=text+getText(root);
  // находим l (номер первого несовпадающего бита)
  // сравниваем k и key
  unsigned l=matchFunc(id,k,key);
  if (isNewNode_=id+l+1!=2*L)
  {
   // берем новый узел
   // если найдены отличия
   // на уровне битов
   unsigned mismatch=k[l]^key[l];
   for (l*=8;(mismatch&0x80)==0;++l,mismatch<<=1);
   // значение несовпадающего бита в k
   const unsigned b=getBitString(k,l);
   // повтор алгоритма P для первых l битов
            if (j_>=l)
   {
       initialize(root);
    run(k,l);
            }
   //
   Node *r=root+id;
   //
   q_->setXlinktag(qid_,r,0);
   //
   r->setXlinktag(b,r,1);
   r->setXlinktag(1ul^b,p_,qtag_);
   //
   r->setSkip(l);
  }
    }
    // функция добавления
    // возвращает true, если узел был добавлен
    bool isNewNode() const
 {
     return isNewNode_;
    }
private:
    bool isNewNode_;
};
//--------------------------------------------------------
// класс добавления нового узла в дерево
// (без проверки на конец)
class PatAddNodeFast
: private PatAddNodeBase
{
public:
    // конструктор, выполняющий добавление немедленно
 PatAddNodeFast(unsigned L,Node *root,
   const unsigned char *text,unsigned id)
  : PatAddNodeBase(root)
 {
  const unsigned char *k=text+id;
  run(k,L*8);
  const unsigned char *key=text+getText(root);
  // находим l (номер первого несовпадающего бита)
  // сравниваем k и key
  unsigned l=matchFunc(id,k,key);
  // берем новый узел
  // если найдены отличия
  // на уровне битов
  unsigned mismatch=k[l]^key[l];
  for (l*=8;(mismatch&0x80)==0;++l,mismatch<<=1);
  // значение несовпадающего бита в k
  const unsigned b=getBitString(k,l);
  // повтор алгоритма P для первых l битов
  if (j_>=l)
  {
   initialize(root);
   run(k,l);
  }
  //
  Node *r=root+id;
  //
  q_->setXlinktag(qid_,r,0);
  //
  r->setXlinktag(b,r,1);
  r->setXlinktag(1ul^b,p_,qtag_);
  //
  r->setSkip(l);
    }
};
//--------------------------------------------------------
// BWT - соритровка
// L - длина сортируемого буфера
// dbuff - буфер длины 2*L, в первой половине данные
// node - выделенная память для хранения узлов, размер L
// в результате получается отсортированный буфер
// возвращаемое начение - положение начального символа
unsigned bwtSort(unsigned L,unsigned char *dbuff,Node *node)
{
 // копировать первую половину во вторую
 std::copy(dbuff,dbuff+L,dbuff+L);
 dbuff[2*L-1]=~dbuff[2*L-1];
 // создать корень
 node->initializeRoot();
 // цикл по байтам буфера
 PatAddNodeBase::initAdd();
 unsigned id;
 for (id=1;id!=L/2+1;++id)
 {
  // добавить узел
  const PatAddNode pan(L,node,dbuff,id);
  if (!pan.isNewNode())
   break;
 }
 if (id==L/2+1)
 {
  for (;id!=L;++id)
   const PatAddNodeFast pan(L,node,dbuff,id);
 }
 const unsigned mult=L/id;
 id=0;
 unsigned zeroId=0;
 PatIterator pit(node);
 do
 {
  pit.runLeft();
  const unsigned offs=pit.getText(node);
  if (offs==0)
   zeroId=id;
  const unsigned char byte=dbuff[offs+L-1];
  for (unsigned id2=0;id2!=mult;++id2)
   dbuff[id++]=byte;
 } while (pit.goRight());
 return zeroId;
}
//--------------------------------------------------------
// обратное BWT - преобразование
// L - длина десортируемого буфера
// dbuff - буфер длины 2*L, в первой половине данные
// pos - положение начального символа (передавать zeroId)
// vect - выделенная память для хранения
// вектора обратного преобразования, размер (0x100+L)
// в результате получается отсортированный буфер
// (во второй половине)
void bwtUnsort(unsigned L,unsigned char *dbuff,
  unsigned pos,unsigned *vect)
{
 unsigned *freq=vect;
 vect+=0x100;
 std::fill(freq,freq+0x100,0);
 unsigned id;
 for (id=0;id!=L;++id)
  ++freq[dbuff[id]];
 unsigned sum=0;
 for (id=0;id!=0x100;++id)
  freq[id]=(sum+=freq[id])-freq[id];
 for (id=0;id!=L;++id)
  vect[freq[dbuff[id]]++]=id;
 for (id=0;id!=L;++id)
  dbuff[L+id]=dbuff[pos=vect[pos]];
}
//--------------------------------------------------------
inline unsigned readSize(const std::string &str)
{
 unsigned num=0;
 for (std::string::const_iterator it=str.begin();
   it!=str.end()&&'0'<=*it&&*it<='9';++it)
 {
  num*=10;
  num+=*it-'0';
 }
 if (it!=str.end())
 {
  switch (*it)
  {
  case 'm':
   num<<=10;
  case 'k':
   num<<=10;
   break;
  }
 }
 return num;
}
//--------------------------------------------------------
// главная функция
int main(int argc,char *argv[])
{
 const char copyRight[]=
  "Burrows-Wheeler sorting program.";
 const char helpStr[]=
  "SORT <e|d> infile outfile [blockSize]\n"
  "\tblockSize (encode only) like 1m == 1024k (default 2m)\n";
 bool allCorrect=true,isDecode;
 if (argc<4)
  allCorrect=false;
 else if (argv[1][1]==0)
 {
  if (argv[1][0]=='e'||argv[1][0]=='E')
   isDecode=false;
  else if (argv[1][0]=='d'||argv[1][0]=='D')
   isDecode=true;
  else
   allCorrect=false;
 }
 else
  allCorrect=false;
 std::cout<<copyRight<<std::endl;
 if (allCorrect)
 {
  HANDLE inh=::CreateFile
   (argv[2]
   ,GENERIC_READ
   ,FILE_SHARE_READ
   ,0
   ,OPEN_EXISTING
   ,FILE_FLAG_SEQUENTIAL_SCAN
   ,0);
  HANDLE outh=::CreateFile
   (argv[3]
   ,GENERIC_WRITE
   ,FILE_SHARE_READ
   ,0
   ,CREATE_ALWAYS
   ,FILE_FLAG_SEQUENTIAL_SCAN
   ,0);
  if (inh==INVALID_HANDLE_VALUE||outh==INVALID_HANDLE_VALUE)
  {
   std::cerr<<"File create error!"<<std::endl;
   return 1;
  }
  unsigned long stub;
  unsigned blockSize=2<<20;
  unsigned long realSize;
  PerformanceTimer tim;
  double secs=0.;
  if (isDecode)
  {
   // unsort
   blockSize=0;
   std::cout<<"Unsort...";
   //
   unsigned char *dbuff=0;
   unsigned *vect=0;
   for (;;)
   {
    ::ReadFile(inh,&realSize,sizeof(realSize),&stub,0);
    if (!stub)
     break;
    if (sizeof(realSize)!=stub)
    {
     std::cerr<<"Defect in input"<<std::endl;
     return 2;
    }
    if (realSize>blockSize)
    {
     if (blockSize)
     {
      ::VirtualFree(vect,0,MEM_RELEASE);
      ::VirtualFree(dbuff,0,MEM_RELEASE);
     }
     dbuff=static_cast<unsigned char*>(
      ::VirtualAlloc(0,2*realSize,
       MEM_COMMIT,PAGE_READWRITE));
     vect=static_cast<unsigned*>(
      ::VirtualAlloc(0,
       (0x100+realSize)*sizeof(unsigned),
       MEM_COMMIT,PAGE_READWRITE));
     blockSize=realSize;
    }
    unsigned zeroId;
    ::ReadFile(inh,&zeroId,sizeof(zeroId),&stub,0);
    if (sizeof(zeroId)!=stub)
    {
     std::cerr<<"Defect in input"<<std::endl;
     return 2;
    }
    ::ReadFile(inh,dbuff,realSize,&stub,0);
    if (realSize!=stub)
    {
     std::cerr<<"Defect in input"<<std::endl;
     return 2;
    }
    tim.start();
    bwtUnsort(realSize,dbuff,zeroId,vect);
    tim.finish();
    secs+=tim.getSeconds();
    ::WriteFile(outh,dbuff+realSize,realSize,&stub,0);
   }
   if (blockSize)
   {
    ::VirtualFree(vect,0,MEM_RELEASE);
    ::VirtualFree(dbuff,0,MEM_RELEASE);
   }
   //
  }
  else
  {
   // sort
   if (argv[4])
    blockSize=readSize(argv[4]);
   std::cout<<"Sort using "<<blockSize<<" block size...";
   //
   unsigned char *dbuff=static_cast<unsigned char*>(
    ::VirtualAlloc(0,2*blockSize,MEM_COMMIT,
     PAGE_READWRITE));
   Node *node=static_cast<Node*>(
    ::VirtualAlloc(0,blockSize*sizeof(Node),
     MEM_COMMIT,PAGE_READWRITE));
   do
   {
    ::ReadFile(inh,dbuff,blockSize,&realSize,0);
    if (realSize)
    {
     tim.start();
     const unsigned zeroId=bwtSort(realSize,dbuff,node);
     tim.finish();
     secs+=tim.getSeconds();
     ::WriteFile(outh,&realSize,sizeof(realSize),&stub,0);
     ::WriteFile(outh,&zeroId,sizeof(zeroId),&stub,0);
     ::WriteFile(outh,dbuff,realSize,&stub,0);
    }
   } while (realSize==blockSize);
   ::VirtualFree(dbuff,0,MEM_RELEASE);
   ::VirtualFree(node,0,MEM_RELEASE);
   //
  }
  ::CloseHandle(outh);
  ::CloseHandle(inh);
  std::cout<<' '<<secs<<" sec."<<std::endl;
 }
 else
  std::cout<<helpStr;
 return 0;
}

perf_tim.h
#pragma once
#include <windows.h>
// таймер производительности
class PerformanceTimer {
public:
 PerformanceTimer(): start_(0), finish_(0) {
  queryFrequency();
  start();
 }
 void start() {
  LARGE_INTEGER large;
  QueryPerformanceCounter(&large);
  start_=large.QuadPart;
 }
 void finish() {
  LARGE_INTEGER large;
  QueryPerformanceCounter(&large);
  finish_=large.QuadPart;
 }
 double getSeconds() const {
  return double(finish_-start_)/double(frequency_);
 }
private:
 static queryFrequency() {
  if (frequency_==0) {
   LARGE_INTEGER large;
   QueryPerformanceFrequency(&large);
   frequency_=large.QuadPart;
  }
 }
 static __int64 frequency_;
 __int64 start_;
 __int64 finish_;
};

perf_tim.cpp
#include "perf_tim.h"

__int64 PerformanceTimer::frequency_=0;


НазадК cодержанию
2006 All Rights Reserved