• [信息论]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> 字符串处理函数,缺乏高效的字符串算法及数据结构支持。

    测试数据

    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
    /* 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****************/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    结果展示

    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
    /* 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****************/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    结果展示

    Test case 1

    C++

    在这里插入图片描述

    Matlab

    在这里插入图片描述

    Test case 2

    C++

    在这里插入图片描述

    Matlab

    在这里插入图片描述

  • 相关阅读:
    用Java语言简单做几个数组相关的练习题吧
    【Vue插件】一款很好用的vue日历插件——vue-sweet-calendar
    RMI协议详解
    C语言学习:5、C语言程序的选择结构
    西门子S7-200SMART 通过向导实现S7通信的具体组态步骤示例
    Kubernetes(k8s)高可用搭建
    Nlog详解---非常详细
    荐书 | 为什么喜欢的女生这么难追?
    java计算机毕业设计电子配件公司仓库管理系统MyBatis+系统+LW文档+源码+调试部署
    MySQL中常见的日志文件
  • 原文地址:https://blog.csdn.net/weixin_49082066/article/details/126928762