LZW算法原理及实现

【数据压缩】LZW编解码原理及算法实现 一、LZW简介 LZW压缩(LZW compression)是一种由Abraham Lempel、Jacob Ziv和Terry Welch发明的基于表查寻算法把文件压缩成小文件的无损压缩方法。 二、LZW编解码举例 1、LZW编码算法思想 举例:输入流 a b b a b a b a c 首先,以ASCII码为基础词典 已编码:a b 遇到a,用97表示,编码为97。 遇到b,用98表示,编码为98。 发现ab,加入词典,ab索引为256。 已编码:a b b a b 遇到b,编码为98,发 阅读详情

LZW算法原理及实现

原理

LZW的想在数据中创建一个短语词典。如果在此后的编码过程中又遇到了相同的字典,则用相应的索引号替代,而不是短语本身。

由于LZW字典产生的规则固定,所以不需要额外传递字典;解码端可以采取逆过程重建出来字典并进行解码。

编码

编码的流程大致如下:

步骤1:将词典初始化为包含所有可能的单字符,当前前缀P初始化为空。

步骤2:当前字符C=字符流中的下一个字符。

步骤3:判断P+C是否在词典中

  • 如果“是”,则用C扩展P,即让P=P+C,返回到步骤2。

  • 如果“否”,则

    输出与当前前缀P相对应的码字W;

    将P+C添加到词典中;

    令P=C,并返回到步骤2

解码

解码的流程大致如下:

步骤1:在开始译码时词典包含所有可能的前缀根。

步骤2:令CW:=码字流中的第一个码字。

步骤3:输出当前缀-符串CW到码字流。

步骤4:先前码字PW=当前码字CW。

步骤5:当前码字CW=码字流的下一个码字。

步骤6:判断当前缀-符串CW 是否在词典中。

  • 如果”是”,则把当前缀-符串CW输出到字符流。

    • 当前前缀P=PW。
    • 当前字符C=当前前缀字符串的第一个字符。
    • 把P+C添加到词典。
  • 如果”否”,则当前前缀P=PW。

    • 当前字符C=当前字符串CW的第一个字符。
    • 输出P+C到字符流,然后把它添加到词典中。

实现

位输入输出工具

因为索引号大于等于255,所以有时候1字节不够的,而是用int型数据又会造成大量字节的浪费,所以要根据索引的最大值合理选择存储所需要的位数,这就要用到按位进行输入输出的工具了。

struct bitWriter
{
    char buffer;
    int pos, bits;
    ofstream &out;
    bitWriter( ofstream &out ): out(out) { buffer = pos = bits = 0;}
    void WriteBit( int bit )
    {
        if( pos == 8 )
        {
            out.write( &buffer, 1 );
            pos = 0; buffer = 0;
        }
        buffer |= ( bit << pos);
        pos ++; bits ++;
    }
    void Write( int value, int len )
    {
        while( len -- )
        {
            WriteBit( value & 1 );
            value >>= 1;
        }
    }
    void EndWrite()
    {
        out.write(&buffer,1);
    }
};
struct bitReader
{
    char buffer;
    int pos, len, bits;
    ifstream &in;
    bitReader( ifstream &in ): in(in)
    {
        buffer = 0; pos = 8;
        in.seekg(0,ios::end);
        len = in.tellg(); bits = len * 8;
        in.seekg(0,ios::beg);
    }
    unsigned int ReadBit()
    {
        if( pos == 8 )
        {
            buffer = in.get();
            pos = 0;
        }
        int t = buffer & 1;
        buffer >>= 1; pos ++;
        return t;
    }
    unsigned int ReadBits( int n )
    {
        unsigned int ans = 0;
        for( int i = 0; i < n; ++ i )
        {
            ans |= ( ReadBit() << i );
        }
        return ans;
    }
};

编码器

利用Trie树来存储字典。分析发现编码所用的节点只需要存储字符的编码和子节点地址即可。

为了减少存储空间的同时保持一定的速度,使用unorder_map来存储节点地址。

最终时间复杂度为 O ( n ) O(n) O(n), n n n为文件长度。

struct encoderNode
{
    int code;
    unordered_map< char, encoderNode* > index;
    encoderNode(){ code = -1; index.clear(); }
    ~encoderNode()
    {
        for( auto i : index )
            if( nullptr != i.second )
                delete i.second;
        index.clear();
    }
};
class LZWEncoder
{
private:
    encoderNode* root;
    encoderNode* pointer;
    int tot, maxValue;
    vector< int > coded_file;
public:
    LZWEncoder() { root = new encoderNode; pointer = root; tot = maxValue = 255;}
    ~LZWEncoder() { delete root; }
    void Init()
    {
        /*构建最初始的字典。*/
        root = new encoderNode; tot = 256;
        pointer = root;
        for( int i = 0; i < 256; ++ i )
        {
            auto t = root->index[i] = new encoderNode;
            t->code = i;
        }
    }
    vector< int > Encode( unsigned char* buffer, int len )
    {
        int pos = 0;
        char c;
        while( pos < len )
        {
            //读取下一个字符
            c = buffer[pos ++];
            //如果当前字符串p+下一个字符不在字典中
            if( nullptr == pointer->index[c] )
            {
                //将当前字符串p的编号输出
                coded_file.push_back( pointer->code );
                //将p+c加入字典
                pointer = ( pointer->index[c] = new encoderNode );
                pointer->code = tot ++;
                pointer = root->index[c];
            }
            else pointer = pointer->index[c]; //如果在字典中就继续读取下一个字符,直到不再其中
        }
        if( len == pos ) coded_file.push_back( pointer->code ); //将未输出的部分输出
        return coded_file;
    }
    vector< int > Encode( ifstream &in )
    {
        //重载,直接从文件流中进行解码
        int pos = 0; char c;
        in.seekg(0, ios::end); int len = in.tellg(); in.seekg(0, ios::beg);
        unsigned char *buffer = new unsigned char[len];
        in.read((char*) buffer, len );
        auto v = Encode( buffer, len );
        delete [] buffer;
        return v;
    }
    vector< int > Encode( bitReader &reader )
    {
        //重载,从位工具中读取数据并解码
        char c; int len = reader.len;
        for( int i = 0; i < len; ++ i )
        {
            //读取下一个字符
            c = reader.ReadBits(8);
            //如果当前字符串p+下一个字符不在字典中
            if( nullptr == pointer->index[c] )
            {
                //将当前字符串p的编号输出
                coded_file.push_back( pointer->code );
                //将p+c加入字典
                pointer = ( pointer->index[c] = new encoderNode );
                pointer->code = tot ++;
                pointer = root->index[c];
            }
            else pointer = pointer->index[c]; //如果在字典中就继续读取下一个字符,直到不再其中
        }
        coded_file.push_back( pointer->code ); //将未输出的部分输出
        return coded_file;
    }
    void PrintToFile( ofstream &out )
    {
        //打印到文件中
        int len = coded_file.size();
        out.write((char*)&len, sizeof(len));
        int symSize = 32;
        out.write((char*)&symSize, sizeof(symSize) );
        for( auto i : coded_file ) out.write((char*)&i, sizeof(i));
    }
    void PrintToFile( bitWriter &writer )
    {
        //重载,使用满足要求的最小bit数打印到文件中
        int len = coded_file.size();
        writer.Write( len, 32 );
        for( auto i : coded_file ) maxValue = max( maxValue, i );
        //存储一个索引所需要的bit数
        int symSize = ceil( log2(maxValue) );
        writer.Write( symSize, 32 );
        for( auto i : coded_file )
            writer.Write( i, symSize );
        writer.EndWrite();
    }
};

解码器

分析发现,解码器只需要直到每个节点的父亲节点即可,通过这种方式来拓展字典也可以得到原始字符串。

struct decoderNode
{
    int character;
    int parent;
};
class LZWDecoder
{
private:
    int tot;
    vector< decoderNode > dict;
    string decoded_file;
public:
    void Init()
    {
        for( int i = 0; i < 256; ++ i ) dict.push_back( {( unsigned char)i, -1});
        tot = 256;
    }
    string GetString( int pos )
    {
        //递归来生成字符串
        string ans;
        if ( dict[pos].parent == -1 )
        {
            //找到开头的字符
            ans += (dict[pos].character);
            return ans;
        }
        ans += GetString( dict[pos].parent );
        //将当前字符加入到最终答案
        ans += (dict[pos].character);
        return ans;
    }
    string Decode( vector< int > &v )
    {
        //p和c分别指当前字符串编号和下一个字符串编号。
        int p = v[0], c;
        int len = v.size();
        for( int i = 1; i < len; ++ i )
        {
            //获取下一个字符串的编号
            c = v[i];
            auto s = GetString(p); decoded_file += s; //将当前字符串加入到最终结果中
            if( c < tot ) dict.push_back( { GetString(c)[0], p } ); //如果当前编号还没有在字典中出现,那就将下一个串的首字符拼接到当前串的后面
            else dict.push_back({s[0], p } ); //否则将当前串的首字母拼接到当前串的后面
            p = c; tot ++;
        }
        decoded_file += GetString(p);
        return decoded_file;
    }
    string Decode( ifstream &in )
    {
        //重载,从文件流中读取文件来进行解码
        int len, symSize; in.read((char*) &len, sizeof(int));
        in.read((char*)&symSize, sizeof(int));
        int p = in.get(), c;
        for( int i = 1; i < len; ++ i )
        {
            //获取下一个字符串的编号
            c = in.get();
            auto s = GetString(p); decoded_file += s; //将当前字符串加入到最终结果中
            if( c < tot ) dict.push_back( { GetString(c)[0], p } ); //如果当前编号还没有在字典中出现,那就将下一个串的首字符拼接到当前串的后面
            else dict.push_back({s[0], p } ); //否则将当前串的首字母拼接到当前串的后面
            p = c; tot ++;
        }
        decoded_file += GetString(p);
        return decoded_file;
    }
    string Decode( bitReader &reader )
    {
        //重载,从位工具中读取数据
        //p和c分别指当前字符串编号和下一个字符串编号。
        int len = reader.ReadBits(32);
        int symSize = reader.ReadBits(32);
        int p = reader.ReadBits(symSize), c;
        for( int i = 1; i < len; ++ i )
        {
            //获取下一个字符串的编号
            c = reader.ReadBits(symSize);
            auto s = GetString(p); decoded_file += s; //将当前字符串加入到最终结果中
            if( c < tot ) dict.push_back( { GetString(c)[0], p } ); //如果当前编号还没有在字典中出现,那就将下一个串的首字符拼接到当前串的后面
            else dict.push_back({s[0], p } ); //否则将当前串的首字母拼接到当前串的后面
            p = c; tot ++;
        }
        decoded_file += GetString(p);
        return decoded_file;
    }
    void PrintToFile( ofstream &out )
    {
        //将解码结果输出到文件
        for( auto i :  decoded_file )
        {
            unsigned char c = i;
            out.write((char*)&c, sizeof(c));
        }
    }
};

实验过程及结果

文本文件测试

利用如下代码进行测试:

void Test1()
{
    string inPath = "test.txt", outPath = "test_encoded.dat";
    ifstream in( inPath, ios::binary ); ofstream out(outPath, ios::binary);
    bitReader reader(in); bitWriter writer(out);
    LZWEncoder encoder; LZWDecoder decoder;
    encoder.Init(); decoder.Init();
    encoder.Encode(in); encoder.PrintToFile(writer);
    in.close(); out.close();

    inPath = "test_encoded.dat", outPath = "test_decoded.txt";
    in.open(inPath, ios::binary); out.open( outPath, ios::binary );
    bitReader decode_reader(in);
    decoder.Decode(decode_reader);
    decoder.PrintToFile(out);
}

在这里插入图片描述
可见编码和解码过程是没有问题的。

同时比较文件大小:
在这里插入图片描述
可以看到文件确实被压缩了,大概压缩了 1 2 \frac{1}{2} 21​左右。

压缩效率分析

使用十个不同类型的文件进行分析。新键一个文件夹存入十个不同类型的文件。
在这里插入图片描述
添加如下代码:

void Test2()
{
    string path = "D:\\CLionProjects\\test";
    string csvPath = "res.csv";
    ofstream csv(csvPath);
    csv << "file name,original bits,compressed bits,compression rate" << endl;
    vector<string> v;
    getAllFiles(path, v);
    for (auto i : v)
    {
        if (i.back() == '.') continue;
        LZWEncoder encoder; encoder.Init();
        ifstream in(i, ios::binary); ofstream out(i + ".dat", ios::binary);
        bitReader reader(in); bitWriter writer(out);
        encoder.Encode(reader); encoder.PrintToFile(writer);
        csv << i.substr(path.size() + 1) << ","
            << reader.bits << ","
            << writer.bits << ","
            << 1.0 - (1.0 * writer.bits / reader.bits) << endl;
        in.close(); out.close();
    }
    csv.close();
}
int main()
{
    Test2();
}

编码后的结果;
在这里插入图片描述
压缩效率分析:
在这里插入图片描述
可以看到,对与某些类型文件如zip,pdf,docx,jpg经过LZW算法之后大小不升反降。首先是他们重复率可能不高,其次LZW算法其实还可以进一步优化,如利用霍夫曼编码对索引进行编码等。
相比之下,LZW算法更适用于大文件(重复的可能性较大),和内容重复率较高的文件。

最终代码

header.h

#pragma once
#include <iostream>
#include <unordered_map>
#include <io.h>
#include <map>
#include <fstream>
#include <string>

using namespace std;

struct bitWriter
{
    char buffer;
    int pos, bits;
    ofstream& out;
    bitWriter(ofstream& out);
    void WriteBit(int bit);
    void Write(int value, int len);
    void EndWrite();
};
struct bitReader
{
    char buffer;
    int pos, len, bits;
    ifstream& in;
    bitReader(ifstream& in);
    unsigned int ReadBit();
    unsigned int ReadBits(int n);
};

struct encoderNode
{
    int code;
    unordered_map< char, encoderNode* > index;
    encoderNode();
    ~encoderNode();
};
class LZWEncoder
{
private:
    encoderNode* root;
    encoderNode* pointer;
    int tot, maxValue;
    vector< int > coded_file;
public:
    LZWEncoder();
    ~LZWEncoder();
    void Init();
    vector< int > Encode(unsigned char* buffer, int len);
    vector< int > Encode(ifstream& in);
    vector< int > Encode(bitReader& reader);
    void PrintToFile(ofstream& out);
    void PrintToFile(bitWriter& writer);
};

struct decoderNode
{
    int character;
    int parent;
};
class LZWDecoder
{
private:
    int tot;
    vector< decoderNode > dict;
    string decoded_file;
public:
    void Init();
    string GetString(int pos);
    string Decode(vector< int >& v);
    string Decode(ifstream& in);
    string Decode(bitReader& reader);
    void PrintToFile(ofstream& out);
};

bitOperator.cpp

#include "header.h"

bitWriter::bitWriter(ofstream& out) : out(out) { buffer = pos = bits = 0; }
void bitWriter::WriteBit(int bit)
{
    if (pos == 8)
    {
        out.write(&buffer, 1);
        pos = 0; buffer = 0;
    }
    buffer |= (bit << pos);
    pos++; bits++;
}
void bitWriter::Write(int value, int len)
{
    while (len--)
    {
        WriteBit(value & 1);
        value >>= 1;
    }
}
void bitWriter::EndWrite()
{
    out.write(&buffer, 1);
}

bitReader::bitReader(ifstream& in) : in(in)
{
    buffer = 0; pos = 8;
    in.seekg(0, ios::end);
    len = in.tellg(); bits = len * 8;
    in.seekg(0, ios::beg);
}
unsigned int bitReader::ReadBit()
{
    if (pos == 8)
    {
        buffer = in.get();
        pos = 0;
    }
    int t = buffer & 1;
    buffer >>= 1; pos++;
    return t;
}
unsigned int bitReader::ReadBits(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans |= (ReadBit() << i);
    }
    return ans;
}

de_encoder.cpp

#include"header.h"
#include <algorithm>

encoderNode::encoderNode() { code = -1; index.clear(); }
encoderNode::~encoderNode()
{
    for (auto i : index)
        if (nullptr != i.second)
            delete i.second;
    index.clear();
}
LZWEncoder::LZWEncoder() { root = new encoderNode; pointer = root; tot = maxValue = 255; }
LZWEncoder::~LZWEncoder() { delete root; }
void LZWEncoder::Init()
{
    /*构建最初始的字典。*/
    root = new encoderNode; tot = 256;
    pointer = root;
    for (int i = 0; i < 256; ++i)
    {
        auto t = root->index[i] = new encoderNode;
        t->code = i;
    }
}
vector< int > LZWEncoder::Encode(unsigned char* buffer, int len)
{
    int pos = 0;
    char c;
    while (pos < len)
    {
        //读取下一个字符
        c = buffer[pos++];
        //如果当前字符串p+下一个字符不在字典中
        if (nullptr == pointer->index[c])
        {
            //将当前字符串p的编号输出
            coded_file.push_back(pointer->code);
            //将p+c加入字典
            pointer = (pointer->index[c] = new encoderNode);
            pointer->code = tot++;
            pointer = root->index[c];
        }
        else pointer = pointer->index[c]; //如果在字典中就继续读取下一个字符,直到不再其中
    }
    if (len == pos) coded_file.push_back(pointer->code); //将未输出的部分输出
    return coded_file;
}
vector< int > LZWEncoder::Encode(ifstream& in)
{
    //重载,直接从文件流中进行解码
    int pos = 0; char c;
    in.seekg(0, ios::end); int len = in.tellg(); in.seekg(0, ios::beg);
    unsigned char* buffer = new unsigned char[len];
    in.read((char*)buffer, len);
    auto v = Encode(buffer, len);
    delete[] buffer;
    return v;
}
vector< int > LZWEncoder::Encode(bitReader& reader)
{
    //重载,从位工具中读取数据并解码
    char c; int len = reader.len;
    for (int i = 0; i < len; ++i)
    {
        //读取下一个字符
        c = reader.ReadBits(8);
        //如果当前字符串p+下一个字符不在字典中
        if (nullptr == pointer->index[c])
        {
            //将当前字符串p的编号输出
            coded_file.push_back(pointer->code);
            //将p+c加入字典
            pointer = (pointer->index[c] = new encoderNode);
            pointer->code = tot++;
            pointer = root->index[c];
        }
        else pointer = pointer->index[c]; //如果在字典中就继续读取下一个字符,直到不再其中
    }
    coded_file.push_back(pointer->code); //将未输出的部分输出
    return coded_file;
}
void LZWEncoder::PrintToFile(ofstream& out)
{
    //打印到文件中
    int len = coded_file.size();
    out.write((char*)&len, sizeof(len));
    int symSize = 32;
    out.write((char*)&symSize, sizeof(symSize));
    for (auto i : coded_file) out.write((char*)&i, sizeof(i));
}
void LZWEncoder::PrintToFile(bitWriter& writer)
{
    //重载,使用满足要求的最小bit数打印到文件中
    int len = coded_file.size();
    writer.Write(len, 32);
    for (auto i : coded_file) maxValue = max(maxValue, i);
    int symSize = ceil(log2(maxValue));
    writer.Write(symSize, 32);
    for (auto i : coded_file)
        writer.Write(i, symSize);
    writer.EndWrite();
}

void LZWDecoder::Init()
{
    for (int i = 0; i < 256; ++i) dict.push_back({ (unsigned char)i, -1 });
    tot = 256;
}
string LZWDecoder::GetString(int pos)
{
    //递归来生成字符串
    string ans;
    if (dict[pos].parent == -1)
    {
        //找到开头的字符
        ans += (dict[pos].character);
        return ans;
    }
    ans += GetString(dict[pos].parent);
    //将当前字符加入到最终答案
    ans += (dict[pos].character);
    return ans;
}
string LZWDecoder::Decode(vector< int >& v)
{
    //p和c分别指当前字符串编号和下一个字符串编号。
    int p = v[0], c;
    int len = v.size();
    for (int i = 1; i < len; ++i)
    {
        //获取下一个字符串的编号
        c = v[i];
        auto s = GetString(p); decoded_file += s; //将当前字符串加入到最终结果中
        if (c < tot) dict.push_back({ GetString(c)[0], p }); //如果当前编号还没有在字典中出现,那就将下一个串的首字符拼接到当前串的后面
        else dict.push_back({ s[0], p }); //否则将当前串的首字母拼接到当前串的后面
        p = c; tot++;
    }
    decoded_file += GetString(p);
    return decoded_file;
}
string LZWDecoder::Decode(ifstream& in)
{
    //重载,从文件流中读取文件来进行解码
    int len, symSize; in.read((char*)&len, sizeof(int));
    in.read((char*)&symSize, sizeof(int));
    int p = in.get(), c;
    for (int i = 1; i < len; ++i)
    {
        //获取下一个字符串的编号
        c = in.get();
        auto s = GetString(p); decoded_file += s; //将当前字符串加入到最终结果中
        if (c < tot) dict.push_back({ GetString(c)[0], p }); //如果当前编号还没有在字典中出现,那就将下一个串的首字符拼接到当前串的后面
        else dict.push_back({ s[0], p }); //否则将当前串的首字母拼接到当前串的后面
        p = c; tot++;
    }
    decoded_file += GetString(p);
    return decoded_file;
}
string LZWDecoder::Decode(bitReader& reader)
{
    //重载,从位工具中读取数据
    //p和c分别指当前字符串编号和下一个字符串编号。
    int len = reader.ReadBits(32);
    int symSize = reader.ReadBits(32);
    int p = reader.ReadBits(symSize), c;
    for (int i = 1; i < len; ++i)
    {
        //获取下一个字符串的编号
        c = reader.ReadBits(symSize);
        auto s = GetString(p); decoded_file += s; //将当前字符串加入到最终结果中
        if (c < tot) dict.push_back({ GetString(c)[0], p }); //如果当前编号还没有在字典中出现,那就将下一个串的首字符拼接到当前串的后面
        else dict.push_back({ s[0], p }); //否则将当前串的首字母拼接到当前串的后面
        p = c; tot++;
    }
    decoded_file += GetString(p);
    return decoded_file;
}
void LZWDecoder::PrintToFile(ofstream& out)
{
    //将解码结果输出到文件
    for (auto i : decoded_file)
    {
        unsigned char c = i;
        out.write((char*)&c, sizeof(c));
    }
}

main.cpp

#include <iostream>
#include "header.h"

void Test1()
{
    string inPath = "test.txt", outPath = "test_encoded.dat";
    ifstream in(inPath, ios::binary); ofstream out(outPath, ios::binary);
    bitReader reader(in); bitWriter writer(out);
    LZWEncoder encoder; LZWDecoder decoder;
    
    encoder.Init(); decoder.Init();  encoder.Encode(in);
    encoder.PrintToFile(writer);
    in.close(); out.close();

    inPath = "test_encoded.dat", outPath = "test_decoded.txt";
    in.open(inPath, ios::binary); out.open(outPath, ios::binary);
    bitReader decode_reader(in);
    decoder.Decode(decode_reader);
    decoder.PrintToFile(out);
}
void getAllFiles(string path, vector<string>& files)
{
    long hFile = 0;
    struct _finddata_t fileinfo;
    string p;
    if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
        do {
            files.push_back(p.assign(path).append("\\").append(fileinfo.name));
        } while (_findnext(hFile, &fileinfo) == 0);
        _findclose(hFile);
    }
}
void Test2()
{
    string path = "D:\\CLionProjects\\test";
    string csvPath = "res.csv";
    ofstream csv(csvPath);
    csv << "file name,original bits,compressed bits,compression rate" << endl;
    vector<string> v;
    getAllFiles(path, v);
    for (auto i : v)
    {
        if (i.back() == '.') continue;
        LZWEncoder encoder; encoder.Init();
        ifstream in(i, ios::binary); ofstream out(i + ".dat", ios::binary);
        bitReader reader(in); bitWriter writer(out);
        encoder.Encode(reader); encoder.PrintToFile(writer);
        csv << i.substr(path.size() + 1) << ","
            << reader.bits << ","
            << writer.bits << ","
            << 1.0 - (1.0 * writer.bits / reader.bits) << endl;
        in.close(); out.close();
    }
    csv.close();
}

int main()
{
    Test1();
    Test2();
}
LZW字典压缩算法的实现 1数据压缩分类 2.字典压缩的基本原理 以色列人Lempel与Ziv发现在正文流中词汇和短语很可能会重复出现。当出现一个重复时,重复的序列可以用一个短的编码来代替。压缩程序重复扫描这样的重复,同时生成编码来代替重复序列。随着时间的过去,编码可以用来捕获新的序列。算法必须设计成压缩程序能够在编码和原始数据序列推导出当前的映射。 2.1.LZ77算法 L... 阅读详情

相关推荐

数据压缩之LZW编解码算法实现与分析

实验目的 掌握词典编码的基本原理,用C/C++/Python等语言编程实现LZW解码器并分析编解码算法。 主要设备 安装 Windows 和 Visual Studio 等编程平台的个人计算机。 实验内容 LZW编码原理和实现算法 LZW的编码思想是不断地从字符流中提取新的字符串,通俗地理解为新“词条”,然后用“代号”也就是码字表示这个“词条”。这样一来,对字符流的编码就变成了用码字去替 换字符流,生成码字流,从而达到压缩数据的目的。LZW编码是围绕称为词典的转换表 来完成的。LZW编码器通过管理这个词典

weixin_46393626的博客 512

LZW压缩算法原理解析

最近整理Github上以前胡乱写的代码,发现自己还写过压缩算法,大概是不知道什么时候用来练手的。里面我实现了哈夫曼树,LZW字典和算数编码三种压缩算法,时隔几年几乎没什么印象了,尤其是后两种连原理都基本忘了,所以把它们拎出来整理一下,也算是逼自己做个回忆。 本篇先讲LZW算法,Wiki里给出的介绍和样例其实还不错,不过我在网上并没有找到很...

weixin_33895475的博客 3111

LZW算法的原理与实现

LZW算法又叫“串表压缩算法”就是通过建立一个字符串表,用较短的代码来表示较长的字符串来实现压缩。 1.基本原理 提取原始文本文件数据中的不同字符,基于这些字符创建一个编译表,然后用编译表中的字符的索引来替代原始文本文件数据中的相应字符,减少原始数据大小。看起来和调色板图象的实现原理差不多,但是应该注意到的是,我们这里的编译表不是事先创建好的,而是根据原始文件数据动态创建的,解码时还要从已编码的数...

Tinsley的博客 3423

LZW压缩算法——简明原理与实现

LZW和哈夫曼编码一样,是无损压缩中的一种。该算法通过建立字典,实现字符重用与编码,适用于source中重复率很高的文本压缩。本文首先讲下LZW的编解码原理,然后给出LZW的实现code。 *********************原理********************* 编码: 编码0-255用来存储Ascii码为[0,255]的字符,放在字典里。编码从256开始,将

Rachel Zhang的专栏 5万+

LZW解码原理和实现算法及效率评估

LZW解码原理和实现算法* : LZW解码算法开始时,译码词典和编码词典相同,包含所有可能的前缀根。具体解码算法如下: 步骤1:在开始译码时词典包含所有可能的前缀根。 步骤2:令CW:=码字流中的第一个码字。 步骤3:输出当前缀-符串string.CW到码字流。 步骤4:先前码字PW:=当前码字CW。 步骤5:当前码字CW:=码字流的下一个码字。 步骤6:判断当前缀-符串string.CW 是否在词典中。 (1)如果”是”,则把当前缀-符串string.CW输出到字符流。 当前前缀P:=先前缀-符串s.

weixin_46805592的博客 340

算法之LZW压缩算法:原理、实现与应用详解

LZW(Lempel - Ziv - Welch)压缩算法是一种基于字典的自适应数据压缩算法,它的核心原理是通过维护一个字符串与编码的编译表,将输入数据中的字符串替换为对应的编码,从而实现数据压缩。与其他压缩算法不同,LZW算法在压缩过程中动态地构建字典,不断识别新的字符串模式,并为其分配新的编码。

wj_rdk的博客 1762

LZW编解码算法的基本原理及其C++实现

LZW编解码算法的基本原理及其C++实现 LZW编码(LZW Encoding)又称“串表压缩算法”,由J.Ziv和A.Lempel在1978年首次介绍,并由Terry A.Welch在1984年予以改进,最终该编码方法由三人的名字命名。 该编码方法属于词典压缩编码方法。词典编码是一种通用编码方法,适用于无法观察新源统计特性,或虽然可观察但统计特性不固定的情形。 LZW编码可应用于通用文件压缩(如...

szzheng的博客 4835

深入浅出LZW算法:从原理到实战编码实现

本文深入浅出地解析了LZW无损数据压缩算法的核心原理与实现。通过生动的比喻和详细的步骤拆解,阐释了其动态构建字典、用短编码替换重复字符串的工作机制,并提供了从Python原理演示到C语言位操作优化的完整实战编码实现,帮助开发者彻底掌握这一经典算法。

weixin_29214199的博客 239

LZW压缩算法原理解析与实现【转载】

LZW算法原理--Wikipedia相关介绍 一个简单的例子 ZW编码 (Encoding) 的核心思想其实比较简单,就是把出现过的字符串映射到记号上,这样就可能用较短的编码来表示长的字符串,实现压缩,例如对于字符串: ABABAB 可以看到子串AB在后面重复出现了,这样我们可以用一个特殊记号表示AB,例如数字2,这样原来的字符串就可以表示为: AB22 这里我们称2是字串AB的记号(Sy...

Jensen Lee的博客 1897

LZW压缩算法的Java实现

该示例代码实现了LZW压缩算法的核心逻辑,并提供了一个简单的测试用例。你可以将需要压缩的数据通过调用compress方法进行压缩,并通过调用decompress方法对压缩后的数据进行解压缩。本文介绍了LZW压缩算法的Java实现,并提供了相应的源代码。LZW算法是一种常用的无损压缩算法,通过构建和管理字典来实现数据的高效压缩。LZW(Lempel-Ziv-Welch)是一种常用的无损压缩算法,它通过构建和管理字典来实现数据的有效压缩。在本文中,我们将探讨LZW压缩算法的Java实现,并提供相应的源代码。

ByteWhiz的博客 449

AnimatedGifEncoder、LZWEncoder、GifDecoder、NeuQuant

java处理gif的几个类(AnimatedGifEncoder、LZWEncoder、GifDecoder、NeuQuant),网上搜集的打成jar包方便使用

LZW for GIF 算法原理和实现

http://blog.youkuaiyun.com/whycadi/article/details/760576     废话少说。先说LZW for GIF 的原理。        LZW是一个字典式压缩算法,他在压缩原始数据时,对每一个新出现的原始数据串赋一个数值作为标号,那么下

lansong1的专栏 554

UA-DETRAC数据集转YOLO格式

UA-DETRAC转YOLO

lafsca5的博客 9107
上一篇: 利用频率陷波滤波处理图像
下一篇: DPCM差分预测编码实验报告
Cross_Entropy
博客等级 码龄7年 69粉丝 45原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值