[信息论]LZW编解码的实现(基于C++&Matlab实现)

LZW编码

算法分析

在这里插入图片描述

程序设计要点:

编码过程主要难点在于

  • 如何设计符合算法流程图所要求的词典?
  • 如何根据所给单词快速找出其在词典中的位置?

对于第一个问题,实质上解决的方法是通过数据结构类型的选择,建立一个以码字为下标、字符串为内容的线性表。这在 M a t l a b Matlab Matlab 中可以通过构造元胞字符串数组实现,在 C C C++ 中可通过 S T L STL STL V e c t o r Vector Vector 向量容器直接实现。然而,线性数据结构虽然实现容易,但我们需要考虑到这样一个问题:实际的操作中需要压缩的句子可能很长,如果我们以线性表简单粗暴地建立词典,是最好的选择吗?答案是否定的。这个原因可以通过对问题二的分析得到答案。通过算法流程图的分析可知,算法流程中需要完成查找单词 W 1 W1 W1 是否在词典中与找到单词 W W W 的码字这两个操作。这两种操作共同的特点是:它们需要遍历整个词典。如果我们使用元胞数组或者 V e c t o r Vector Vector 存储时,最直接的想法是,以期望查找的单词为参考对象,遍历整个词典,逐一核对是否与对应词典内容相等,这种做法的时间复杂度是 O ( n ) O(n) O(n), 也就是说,查找的效率将受制于词典生长的程度,显然,当待压缩讯息很长时,词典的生长会很迅速,查找某个单词的时间开销将会增大。有些算法书上会说线性表查找可以使用二分法,时间复杂度就能降低至 O ( l o g n ) O(logn) O(logn),但遗憾的是, L Z W LZW LZW 编码算法中的词典并不满足严格的字典序,因此无法使用二分查找。实际上,有一种数据结构可以实现 L Z W LZW LZW 词典的 O ( l o g n ) O(logn) O(logn) 查找,那就是 C C C++ S T L STL STL 中的关联容器 S e t Set Set M a p Map Map S e t Set Set M a p Map Map 容器的底层实现原理是基于红黑树,它是一种非常高效的平衡检索二叉树,其查找某个节点元素的效率正是 O ( l o g n ) O(logn) O(logn) ,符合我们的需求。此外, S e t Set Set 容器的数学意义与数学中的集合概念等价,即存在自动去重的功能;而 M a p Map Map 容器可以实现任意数据类型之间的映射,比如建立以字符串为下标,整数为存储数据类型的数组,可以很好地满足我们的需求。最后,本题中个人认为, C C C++ 处理较 M a t l a b Matlab Matlab 处理更优, M a t l a b Matlab Matlab 对字符串的处理还是比较弱的,仅有字符数组、二维字符数组、元胞数组以及一些基础的 < c s t r i n g > <cstring> <cstring> 字符串处理函数,缺乏高效的字符串算法及数据结构支持。

测试数据

Test case 1

输入数据

A B C A B D A B C A A A A B B B A B C A B C A ABCABDABCAAAABBBABCABCA ABCABDABCAAAABBBABCABCA

输出数据

0 x 41   0 x 42   0 x 43   0 x 100   0 x 44   0 x 100   0 x 102   0 x 41   0 x 107   0 x 42   0 x 109   0 x 105   0 x 10 b 0x41\ 0x42\ 0x43\ 0x100\ 0x44\ 0x100\ 0x102\ 0x41\ 0x107\ 0x42\ 0x109\ 0x105\ 0x10b 0x41 0x42 0x43 0x100 0x44 0x100 0x102 0x41 0x107 0x42 0x109 0x105 0x10b

Test case 2

输入数据

101001101 101001101 101001101

输出数据

2   1   3   4   3   2 2\ 1\ 3\ 4\ 3\ 2 2 1 3 4 3 2

C++ Code

/**
  ******************************************************************************
  * @file    Project/Template/LZW_Encode.cpp
  * @author  2019051312 LZH
  * @version V1.0.0
  * @date    29-March-2022
  * @brief   Main program body
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include<bits/stdc++.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ll long long
#define ld long double
#define pb push_back
using namespace std;
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
string s,s_current; //s_next:memorized word.
set<string> Dick; //Dictionary
map<string,int> addr; //Address of a word
int p=0; //String pointer
int Dick_size; //Size of dictionary.
/* Private function prototypes -----------------------------------------------*/
/* Private functions -----------------------------------------------*/

/**
  * @brief  Test case 1 Initialization.
  * @param  None
  * @param  None
  * @retval None
  */
void Init1(){
	cin>>Dick_size;
	cin>>s;
	Dick.clear();
	s_current.clear();
	for (int i=0; i<256; i++){
		string tmp;
		tmp.clear();
		tmp+=(char)i;
		Dick.insert(tmp);
		addr[tmp]=i;
	}
	
	s_current+=s[0];
	p++;
}  
  
/**
  * @brief  Test case 2 Initialization.
  * @param  None
  * @param  None
  * @retval None
  */
void Init2(){
	cin>>Dick_size;
	cin>>s;
	Dick.clear();
	s_current.clear();
	for (int i=0; i<2; i++){
		string tmp;
		tmp.clear();
		tmp+='0'+i;
		Dick.insert(tmp);
		addr[tmp]=i+1;
	}
	
	s_current+=s[0];
	p++;
}
/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(){
	ios::sync_with_stdio(0); 
	Init1(); //for test case 1.
	//Init2(); //for test case 2.
	while (p<s.length()){
		set<string>::iterator it;
		string s_new;
		s_new=s_current+s[p]; //Generate the new word.
		it=Dick.find(s_new); //Find whether or not the new word is in the dictionary.
		if (it==Dick.end()){ //if new word not exists in the dictionary or overflow.
			cout<<"0x"<<hex<<addr[s_current]<<endl; //Print the code of current word.
			//add a new word to the dictionary if not overloaded.
			if (Dick.size()<Dick_size){
				Dick.insert(s_new);
				addr[s_new]=Dick.size()-1; 
				//addr[s_new]++; //for test case 2.
			}
				
			//Update current word via current read char
			s_current.clear();
			s_current+=s[p];
		}
		else{
			//Update current word via new word.
			s_current=s_new;
		}
		p++; //Iterate to next char
	}
	cout<<"0x"<<hex<<addr[s_current]<<endl;
	return 0;
}

/**
  * @}
  */


/************************END OF FILE****************/

Matlab Code

s='ABCABDABCAAAABBBABCABCA'; %s='101001101';
s_len=23; %s_len=9;
s_new='';
base=255; %base=1;
p=1;
pp=1;
Dick=cell(500);
for ii=0:base
    tmp=char(ii); %tmp=char(48+ii);
    Dick{pp}=tmp;
    pp=pp+1;
end

s_current=s(p);
p=p+1;

while (p<=s_len) 
   s_new=strcat(s_current,s(p));
    tmp_p=-1;
    for ii=1:pp-1
        if (strcmp(s_new,Dick(ii)))
            tmp_p=ii;
        end
    end
    
    if (tmp_p==-1)
        for ii=1:pp-1
            if (strcmp(s_current,Dick(ii)))
                tmp_p=ii-1;
                %tmp_p=ii;
            end
        end
        fprintf('%x ',tmp_p);
        Dick{pp}=s_new;
        pp=pp+1;
        s_current=s(p);
    else
        s_current=s_new;
    end
    p=p+1;
end

tmp_p=-1;
for ii=1:pp-1
    if (strcmp(s_current,Dick(ii)))
       tmp_p=ii-1;
       %tmp_p=ii;
    end
end

fprintf('%x\n',tmp_p);

结果展示

Test case 1

C++

在这里插入图片描述

Matlab

在这里插入图片描述

Test case 2

C++

在这里插入图片描述

Matlab

在这里插入图片描述

LZW解码

算法分析

在这里插入图片描述

程序设计要点:

解码过程的重点主要在于

  • 如何快速找出词典中是否存在读入码字所对应的单词?

虽然解码过程中也有“遍历词典、查找单词”的操作,但这个过程是与上述编码过程中的操作逻辑不同的。编码过程中查找单词的逻辑是:已知一个新的未知的字符串,要查找整个词典内是否有已知字符串单词与其匹配,所以无论使用怎样的数据结构维护词典或是使用怎样的查找算法,最坏情况都是需要遍历整个词典。然而,在解码过程中,我们输入的是单词对应的码字,同时我们已知码字的作用在词典中是表示单词元素的下标,因此很显然我们可以通过直接比较码字大小与词典空间大小进行比较快速判断单词在词典中的存在性,即若码字 x x x 小于等于词典 S i z e Size Size, 则存在,否则不存在。这种查找算法的时间复杂度是 O ( 1 ) O(1) O(1),十分完美的常数复杂度。解码过程与编码过程查找单词的差异主要在于:在一个无序词典中,单词元素是不具有单调性的,而码字(数组下标元素)是具有单调递增性的,因而可以直接比较 x x x S i z e Size Size

测试数据

Test case 1

输入数据

0 x 41   0 x 42   0 x 43   0 x 100   0 x 44   0 x 100   0 x 102   0 x 41   0 x 107   0 x 42   0 x 109   0 x 105   0 x 10 b   0 x f f f 0x41\ 0x42\ 0x43\ 0x100\ 0x44\ 0x100\ 0x102\ 0x41\ 0x107\ 0x42\ 0x109\ 0x105\ 0x10b\ 0xfff 0x41 0x42 0x43 0x100 0x44 0x100 0x102 0x41 0x107 0x42 0x109 0x105 0x10b 0xfff

输出数据

A B C A B D A B C A A A A B B B A B C A B C A ABCABDABCAAAABBBABCABCA ABCABDABCAAAABBBABCABCA

Test case 2

输入数据

2   1   4   5   2   7   8   0 x f f f 2\ 1\ 4\ 5\ 2\ 7\ 8\ 0xfff 2 1 4 5 2 7 8 0xfff

输出数据

1000000111111 1000000111111 1000000111111

C++ Code

/**
  ******************************************************************************
  * @file    Project/Template/LZW_Decode.cpp
  * @author  2019051312 LZH
  * @version V1.0.0
  * @date    29-March-2022
  * @brief   Main program body
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include<bits/stdc++.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ll long long
#define ld long double
#define pb push_back
using namespace std;
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
int x; //code
string s; //s:answer
vector<string> Dick; //Dictionary
int code_current; //memorized the current code.
/* Private function prototypes -----------------------------------------------*/
/* Private functions -----------------------------------------------*/

/**
  * @brief  Test case 1 Initialization.
  * @param  None
  * @param  None
  * @retval None
  */
void Init1(){
	cin>>hex>>x; //input the first code.
	s.clear();
	Dick.clear();
	for (int i=0; i<256; i++){ 
		string tmp;
		tmp.clear();
		tmp+=(char)i; 
		Dick.pb(tmp);
	}

	code_current=x;
	s+=Dick[x];
}

/**
  * @brief  Test case 2 Initialization.
  * @param  None
  * @param  None
  * @retval None
  */
void Init2(){
	cin>>hex>>x; //input the first code.
	s.clear();
	Dick.clear();
	Dick.pb("114514"); 
	for (int i=0; i<2; i++){ 
		string tmp;
		tmp.clear();
		tmp+='0'+i; 
		Dick.pb(tmp);
	}

	code_current=x;
	s+=Dick[x];
}
/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(){
	ios::sync_with_stdio(0); 
	Init1(); //for test case 1.
	//Init2(); //for test case 2. 
	string s1,s_new;
	while (1){
		cin>>hex>>x;
		if (x==0xfff) break;
		if (x<Dick.size()){ // if current word exists.
			s_new=Dick[x];
			s+=s_new; //Print the word first.
			//Update the dictionary.
			s_new=Dick[code_current]+s_new[0];
			Dick.pb(s_new);
			code_current=x; //memorize the code of the new-input word.
		}
		else{
			//Update the dictionary first.
			s_new=Dick[code_current]+Dick[code_current][0]; //append the first char of book[code_current] to the end of it.
			Dick.pb(s_new);
			s+=s_new; //add s_new to the answer.
			code_current=Dick.size()-1; //memorize the code of the new-constructed word.
		}
	}
	cout<<s<<endl; //Print the decoding answer.
	return 0;
}
/**
  * @}
  */
/************************END OF FILE****************/

Matlab Code

%for test case 1.
%x=[0x041,0x042,0x043,0x100,0x044,0x100,0x102,0x041,0x107,0x042,0x109,0x105,0x10b,0xfff];
%for test case 2.
x=[0x002,0x001,0x004,0x005,0x002,0x007,0x008,0xfff];
s='';
s_new='';
code_current=0;
base=1; %for test case 2.
%base=255; % for test case 1.
p=1;
pp=0;
Dick=cell(500);
for ii=0:base
    pp=pp+1;
    %tmp=char(ii); % for test case 1.
    tmp=char(48+ii); %for test case 2.
    Dick{pp}=tmp;
end

code_current=x(p);
%code_current=code_current+1; %for test case 1.
s=char(Dick(code_current));

while (1)
    p=p+1;
    if (x(p)==0xfff) 
        break;
    end
    
    if (x(p)<=pp) % for test case 1, x(p)<pp.
       % x(p)=x(p)+1; %for test case 1.
        s_new=char(Dick(x(p)));
        s=strcat(s,s_new);
        s_new=strcat(char(Dick(code_current)),s_new(1));
        pp=pp+1;
        Dick{pp}=s_new;
        code_current=x(p);
    else
        tmp=char(Dick(code_current));
        s_new=strcat(char(Dick(code_current)),tmp(1));
        pp=pp+1;
        Dick{pp}=s_new;
        s=strcat(s,s_new);
        code_current=pp;
    end
end
disp(s);

结果展示

Test case 1

C++

在这里插入图片描述

Matlab

在这里插入图片描述

Test case 2

C++

在这里插入图片描述

Matlab

在这里插入图片描述

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值