Compression

Post date: Nov 27, 2013 3:28:30 AM

سلام

چند روز قبل برای یه دوست، برنامه «فشرده سازی با استفاده از کد هافمن» را نوشتم. یک متن داریم که از کاراکترهای مشخصی استفاده می‌کند. مثلا این متن را در نظر بگیرید:

"aabaaxyxbaxaa"

می‌خواهیم این متن را به روش هافمن فشرده کنیم. اول باید کد صفر و یکی هر کاراکتر رو پیدا کنیم و بعد به جای هر کاراکتر کد مربوط به آن را بگذاریم. اول یک جدول از کاراکترها و تعداد تکرارشان درست می‌کنیم:

حالا برای به دست آوردن کد مربوط به هر کاراکتر، باید درخت دو دویی‌ای به دست بیاوریم که کدها را مشخص می‌کند. برای به دست آوردن این درخت از درخت‌های تک نقطه‌ای شروع می‌کنیم و به درخت‌های بزرگ تر می‌رسیم:

از بین این‌ها، دو تا را که کم ترین مقدار را دارند انتخاب می‌کنیم و از آن‌ها یک درخت دو دویی می‌سازیم:

مقدار سرشاخه اضافه شده برابر با مجموع مقدار ریشه‌هایش است. یعنی 3 = 2 + 1. همیشه ریشه سمت چپ کم تر (یا مساوی با) ریشه سمت راست است.

بعد دقیقا همین کار و سرشاخه‌های باقی مانده تکرار می‌کنیم. یعنی دو تا با کمترین مقدار را انتخاب می‌کنیم و از آن‌ها یک درخت بزرگ تر می‌سازیم:

و دوباره این کار را می‌کنیم تا فقط یک درخت باقی بماند:

توجه کنید که در هر مرحله دو درخت با کم ترین مقدار انتخاب می‌شود و یک درخت بزرگ تر درست می‌شود که طرف چپش درخت با مقدار کمتر و طرف راستش درخت با مقدار بیش تر قرار دارد و مقدار خود درخت هم حاصل جمع مقدارهای دو درخت اولیه است. و این کار آن قدر تکرار می‌شود تا فقط یک درخت باقی بماند.

با این درخت کد مربوط به هر کاراکتر مشخص می‌شود:

حالا متن "aabaaxyxbaxaa" به این صورت فشرده می‌شود: 1101111000100001110011 که در سه بایت جا می‌گیرد. یعنی 13 کاراکتر در 3 کاراکتر فشرده می‌شود.

البته فشرده سازی برای متن‌های طولانی معنی پیدا می‌کند. چون علاوه بر بیت‌هایی که به دست آمد جدول بالا هم باید ذخیره بشود که آن هم خودش جا می‌خواهد.

حالا قرار است برنامه‌ای بنویسیم که یک متن رو به روشی که گفتم فشرده کند.

من برای نوشتن این برنامه اول تصمیم گرفتم درخت‌های دو دویی رو شبیه سازی کنم. کار اصلی یک برنامه نویس شبیه سازی است. این کار رو به وسیله یک کلاس انجام دادم که خواص بازگشتی دارد. بعد، کلاسی برای فشرده سازی نوشتم:

#include <conio.h>

#include <string>

#include <vector>

#include <map>

#include <iostream>

using namespace std;

class BinTree // این کلاس درخت دو دویی را شبیه سازی میکند

{
public:

BinTree* left; // یال سمت چپ

BinTree* right; // یال سمت راست

int node;

char reserved_char; // برای ضبط کاراکتر وقتی درخت فقط یک نقطه است یعنی پایین ترین قسمت است

vector<bool> reserved_bins; // کدی صفر و یکی مربوط به کاراکتر بالا

//-----------------------

private:

void SetNode(int _node) // مقدار یک گره یعنی نقطه

{

node = _node;

}

void SetLeft(BinTree* _left) // تعیین یال چپ

{

left = _left;

}

void SetRight(BinTree* _right) // تعیین یال راست

{

right = _right;

}

//-----------------------

bool isSimple() // آیا درخت فقط یک نقطه است؟

{

return (left == 0);

}

int GetLeftNode() // مقدار عددی نقطۀ طرف چپ

{

return left -> node;

}

int GetRightNode() // مقدار عددی نقطۀ طرف راست

{

return right -> node;

}

//-----------------------

bool ContainTree(BinTree* subtree) // آیا این پارامتر بخشی از درخت اصلی است؟

{

if(subtree == this)

return true;

if(isSimple())

return false;

if(left -> ContainTree(subtree))

return true;

if(right -> ContainTree(subtree))

return true;

return false;

}

public:

vector<bool> GetBin(BinTree* subtree) // گرفتن کد صفر و یک هر نقطۀ تحتانی از درخت با استفاده از درخت

{

vector<bool> ret;

if(subtree == this)

return ret;

if(isSimple())

{

cout<< "Error in GetBin\n";

return ret;

}

if(left -> ContainTree(subtree))

{

ret.push_back(false);

vector<bool> v = left -> GetBin(subtree);

unsigned len = v.size();

for(unsigned i = 0; i < len; i++)

ret.push_back(v[i]);

}

if(right -> ContainTree(subtree))

{

ret.push_back(true);

vector<bool> v = right -> GetBin(subtree);

unsigned len = v.size();

for(unsigned i = 0; i < len; i++)

ret.push_back(v[i]);

}

return ret;

}

//-----------------------

void Print() // چاپ درخت . از این تابع استفاده نمی شود

{

cout<< node;

if(!isSimple())

{

cout<< "[";

left -> Print();

cout<< " : ";

right -> Print();

cout<< "]";

}

}

//-----------------------

BinTree(int _node)

{

SetNode(_node);

SetLeft(0);

SetRight(0);

}

BinTree(int _node, BinTree* _0, BinTree* _1)

{

SetNode(_node);

SetLeft(_0);

SetRight(_1);

}

};

class Compress // کلاسی که یک رشته می گیرید و به روش هافمن فشرده می کند

{
public:

string text;

vector<bool> bins;// برداری که متن بعد از تبدیل شدن به صفر و یک درش قرار میگیره. تا صفر و یک ها رو داشته باشیم.

private:

BinTree* tree; // the bintree made

typedef map<char,unsigned> MAP; //MAP تعریف

typedef MAP::iterator IT; //IT تعریف

typedef pair<char,unsigned> PAIR;

MAP char_count; // کاراکترها و تعداد آنها

vector<BinTree*> bintrees;

vector<BinTree*> simples;

vector<BinTree*> created_bintrees;// reserved for freeing memory

bool Repetitive(char ch) // مشخص کنندۀ تعداد تکرار کاراکتر

{

for (IT it = char_count.begin(); it != char_count.end(); it++)

{

if(it -> first == ch)

return true;

}

return false;

}

void FillMap()//char_count کامل کنندۀ

{

char_count.empty(); // protecting code

unsigned len = text.length();

for(unsigned i = 0; i < len; i++)

{

char ch = text[i];

if(Repetitive(ch))

char_count[ch]++;

else

char_count.insert(PAIR(ch,1));

}

}

//------------------------------------------------------------------

int Node(unsigned i)

{

return bintrees[i] -> node;

}

unsigned TreesNumber() // تعداد درختهایی که قرار است به هم متصل شوند

{

return bintrees.size();

}

void CombineTrees() // متصل کردن دو در خت مناسب از درخت های باقی مانده

{

unsigned n = TreesNumber();

if(n <= 1) // protecting abnormal use

return;

unsigned i_min1 = 0;

unsigned i_min2 = 0;

for(unsigned i = 0; i < n; i++) // minimum offset

{

if(Node(i) < Node(i_min1))

i_min1 = i;

}

for(unsigned i = 0; i < n; i++) // second minimum offset

if(i != i_min1)

{

if(Node(i) < Node(i_min2))

i_min2 = i;

}

if(i_min1 == i_min2)

i_min2++;

int node = Node(i_min1) + Node(i_min2); // new node for new_tree

BinTree* left = bintrees[i_min1]; // left for new_tree

BinTree* right = bintrees[i_min2]; // right for new_tree

BinTree* new_tree = new BinTree(node,left,right); // new bintree

bintrees[i_min1] = new_tree; // replace offset i_min1

bintrees.erase(bintrees.begin() + i_min2); // delete offset i_min2

created_bintrees.push_back(new_tree); // reserved for freeing memory

}

void CreateBinTree() // ایجاد درخت اصلی بزرگ با استفاده از کاراکترها و تعداد آنها

{

FillMap();

for (IT it = char_count.begin(); it != char_count.end(); it++)

{

bintrees.empty(); // protecting code

BinTree* simple = new BinTree(it -> second);

simple -> reserved_char = it -> first;

bintrees.push_back(simple);

simples.push_back(simple);

created_bintrees.push_back(simple); // reserved for freeing memory

}

while(TreesNumber() > 1) // combine bintrees to get one

CombineTrees();

}

void GetTree() // تولید و انتساب درخت اصلی

{

CreateBinTree();

tree = bintrees[0];

}

void FindBinCodes() // قرار دادن کد مربوط به هر کاراکتر به درخت تک نقطۀ مربوط به آن

{

unsigned simlen = simples.size();

for(unsigned i = 0; i < simlen; i++)

simples[i] -> reserved_bins = tree -> GetBin(simples[i]);

}

vector<bool> GetCode(char ch) // به دست آوردن کد هر کاراکتر به صورت آرایه

{

unsigned simlen = simples.size();

for(unsigned i = 0; i < simlen; i++)

{

if(simples[i] -> reserved_char == ch)

return simples[i] -> reserved_bins;

}

cout<< "Error in GetCode()" "\n"; // protecting code

vector<bool> ret;

return ret;

}

void ConvertText() // تبدیل رشته با کد هافمن

{

unsigned size = text.size();

for(unsigned i = 0; i < size; i++)

{

vector<bool> v = GetCode(text[i]);

unsigned v_len = v.size();

for(unsigned j = 0; j < v_len; j++)

bins.push_back(v[j]);

}

}

//------------------------------------------------------------------

public:

void Print() // چاپ اطلاعات

{

cout<< "original text:\n \"" << text << "\"" "\n";

cout<< "\n" "Code Table:" "\n\n";

unsigned simlen = simples.size();

for(unsigned i = 0; i < simlen; i++)

{

char ch = simples[i] -> reserved_char;

vector<bool> v = simples[i] -> reserved_bins;

cout<< ch << " : ";

unsigned v_len = v.size();

for(unsigned j = 0; j < v_len; j++)

cout<< v[j];

cout<< "\n";

}

cout<< "\n\n" "zipped text bits: " "\n\n";

unsigned len = bins.size();

for(unsigned i = 0; i < len; i++)

cout<< bins[i];

}

Compress(string text): text(text) // سازنده

{

GetTree();

FindBinCodes();

ConvertText();

}

~Compress() // نابود کنندۀ درخت های ایجاد شده

{

unsigned len = created_bintrees.size();

for(unsigned i = 0; i < len; i++)

delete created_bintrees[i];

}

};

int main()

{

string text = "aabaaxyxbaxaa"; // رشته ای که قرار است فشرده شود

Compress com(text);

com.Print();

_getch();

}

Output:

original text:

"aabaaxyxbaxaa"

Code Table:

a : 1

b : 011

x : 00

y : 010

zipped text bits:

1101111000100001110011