數據壓縮的重要組成部分---位操作


數據壓縮描述

數據壓縮是一個減少數據存儲空間的過程。

數據壓縮包括兩個過程:一個過程是,壓縮或編碼數據,數據大小減小;另一個過程是,解壓縮或解碼數據,還原到數據本身的狀態。

根據信息的內容,所有的數據都會表現出一定的特性,稱為熵(從熱力學借用的一個術語)。壓縮是可能的,因為絕大多數數據所表現出來的容量都大於其熵所建議的最佳容量。為了衡量壓縮的效率,通常用1減去壓縮數據大小除以原始數據大小的值。這個值稱為數據的壓縮率。

從廣義上講,數據壓縮的方法分為兩大類:有損壓縮和無損壓縮。在有損壓縮中,我們接受數據有一定的損失來換取更大的壓縮比。在某些應用中,一定的損失是可以接受的,比如圖像或音頻的處理,因為這種損失不會影響其效果並且會受到嚴格控制。然而,我們通常使用的是無損壓縮,它能夠保證解壓縮時准確的還原原始數據。

我們重點介紹無損壓縮,實現無損壓縮主要有兩種方法:最小冗余編碼和基於字典的方法。最小冗余編碼使用更少的位對出現更為頻繁的字符進行編碼,用較長的位對出現頻繁較低的字符進行編碼。在基於字典的方法中,其通過對數據進行符號編碼,來代替那些重復多余的短語。

數據壓縮的重要組成部分

位操作是數據壓縮的重要組成部分,因為絕大多數方法在某種程度上都需要對數據的位進行操作。C語言本身提供了一些位操作的接口,可以用這些接口來實現一些擴展的位操作類。

我們先來看一下數據壓縮的頭文件(在數據壓縮介紹中必不可少,包括各種符號常量、壓縮、解壓縮的接口等):

/*compress.h  數據壓縮的頭文件*/
#ifndef COMPRESS_H
#define COMPRESS_H

#include "bitree.h"

/*定義霍夫曼樹的節點數據結構*/
typedef struct HuffNode_
{
    unsigned char symbol;
    int           freq;
}HuffNode;

/*定義霍夫曼代碼表中條目的數據結構*/
typedef struct HuffCode_
{
    unsigned char  used;
    unsigned char  code;
    unsigned char  size;
}HuffCode;

/*定義LZ77令牌成員所需要的位數*/
#define LZ77_TYPE_BITS   1
#define LZ77_WINOFF_BITS 12
#define LZ77_BUFLEN_BITS 5
#define LZ77_NEXT_BITS   8

/*定義滑動窗口的大小和LZ77的超前緩沖區.
每個都必須小於或等於2,分別提高到LZ77_WINOFF_BITS和LZ77_BUFLEN_BITS。
*/
#define LZ77_WINDOW_SIZE  4096
#define LZ77_BUFFER_SIZE  32 

/*定義LZ77短語標記的位數*/
#define LZ77_SYMBOL_BITS  (LZ77_TYPE_BITS + LZ77_WINOFF_BITS + LZ77_NEXT_BITS + LZ77_BUFLEN_BITS)

/*函數接口*/
int huffman_compress(const unsigned char *original, unsigned char **compressed, int size);
int huffman_uncompress(const unsigned char *compressed, unsigned char **original);
int lz77_compress(const unsigned char *original, unsigned char **compressed, int size);
int lz77_uncompress(const unsigned char *compressed, unsigned char **original);

#endif // COMPRESS_H

位操作的描述

在壓縮和解壓縮數據時,常常要在小於一個字節的數量級上進行數據操作。所以,在學習各種數據壓縮的方法之前,必須熟悉一些對數據位進行的操作,這非常重要。本節展示的方法包含了對任意位的緩沖區的操作。當然,這里介紹的位操作只是一部分,只是定義了現在數據壓縮和后續的數據加密中所要用到的操作。

位操作的接口定義

bit_get


int bit_get (const unsigned char *bits, int pos);

返回值:相應位所處的狀態:0或1。

描述:獲取緩沖區bits中處於位置pos的位的狀態。緩沖區最左邊的位置為0。返回的狀態值為0或1。

復雜度:O(1)

bit_set


void bit_set (unsigned char *bits, int pos, int state);

返回值:無

描述:設置緩沖區bits中處於位置pos的位的狀態(根據state值來設置)。緩沖區最左邊的位設置為0。狀態值必須為0或1。

復雜度:O(1)

bit_xor


void bit_xor (const unsigned char *bits1, const unsigned char *bits2, unsigned char *bitsx, int size);

返回值:

描述:按位計算兩個緩沖區bits1和bits2的異或值,其中每個緩沖區包含size個位,然后將結果返回bitsx中。異或的過程是將兩個二進制操作數進行運算,如果操作數據處於位置i的兩位相同,得到0;如果處於位置i的兩位不同,則返回1。例如:11010 異或 01011 = 10001。bitsx所需要的存儲空間由函數調用者來管理。

復雜度:O(B),其中B為每個緩沖區中位的個數。

bit_rot_left


void bit_rot_left (unsigned char *bits, int size, int count);

返回值:無

描述:輪轉緩沖區bits(含size位),將位值向左移動count位。此操作完成后,處於最左端的count位移動到緩沖區的最右端,而且其他的位也相應的輪轉。

復雜度:O(nB),其中B為每個緩沖區中位的個數,n為要輪轉到左邊的位數。

位操作的實現與分析

每個位操作都可操作緩沖區中的數據,緩沖區由無符號字符作為指針來指定。該指針指向足夠多的字節來表示緩沖區中的位數。如果緩沖區中的位數不是8的倍數,那么說明最后一個字節的某些位沒有使用。

bit_get

bit_get操作獲取緩沖區中一個位的狀態。要做到這一點,首先要確定位所在的字節,然后通過一個掩碼從字節中獲取相應的位。掩碼中設置為1的位是將要從字節中讀出的位,用一個循環操作將此位移動到適當的位置,通過索引bits中相應的字節,並應用調用后的掩碼,可以獲取所需的位。

bit_get的時間復雜度為O(1)。這是因為獲取緩沖區中的位的狀態所進行的操作都能夠在固定的時間內完成。

bit_set

bit_set 操作設置緩沖區中的一個位的狀態。此操作與bit_get的工作方式相似,只是它是利用掩碼設置指定的位的狀態,而bit_get是獲取指定位的狀態。

bit_set的時間復雜度為O(1)。這是因為獲取緩沖區中位的狀態所進行的所有操作都能夠在固定的時間內完成。

bit_xor

bit_xor對兩個緩沖區bits1和bits2進行按位異或運算,並將計算的結果放到緩沖區bitsx中。要做到這一點,將bits1中第i個位置的位與bits2中第i個位置的位進行比較,如果位值相同,將第i個位置的位置為0;否則,將第i個位置的位置為1。這個過程持續下去直到緩沖區中size指定的每個位都計算完成。

bit_xor的時間復雜度為O(B),其中B是每個緩沖區中的位數。這是因為此操作要在每個位上循環迭代一次。

bit_rot_left

bit_rot_left將緩沖區指定數量的位向左輪轉。首先,保存最左端字節的最左端的位,然后向左一位一位地移動每個字節的位值。在移動字節的過程中,將前一個字節最右邊的位移動到當前字節的最左邊。當處理到最后一個字節時,將其最右邊的位移動到首字節的最高位上。這個過程一直持續下去直到所有的位都輪轉到位。

bit_rot_left的時間復雜度為O(nB),其中n為要向左輪轉的位的個數,B是緩沖區中位的個數。這是因為,對於每次輪轉,要進行(B/8)+1次移動。

 示例:位操作的實現

/*bit.c  位操作的實現*/
#include <stdlib.h>

#include "bit.h"

/*bit_get  獲取緩沖區bits中處於pos位的狀態*/
int bit_get(const unsigned char *bits, int pos)
{
    unsigned char  mask;
    int            i;

    /*設置掩碼*/
    mask = ox80;
    for(i=0; i<(pos % 8); i++)
        mask = mask >> 1;
    /*用位與運算獲取對應的位*/
    return (((mask & bits[(int)(pos / 8)]) == mask)? 1:0)
}
/*bit_set  設置緩沖區bits中位於pos位的狀態*/
void bit_set(unsigned char *bits, int pos, int state)
{
    unsigned char mask;
    int           i;

    /*設置掩碼*/
    mask = ox80;
    for(i=0; i<(pos % 8); i++)
        mask=mask>>1;

    /*依據state設置位*/
    if(state)
        bits[pos/8] = bits[pos/8] | mask;
    else
        bits[pos/8] = bits[pos/8] & (~mask);

    return;
}
/*bit_xor  按位異或運算*/
void bit_xor(const unsigned char *bits1,const unsigned char *bits2,unsigned char *bitsx,int size)
{
    int i;
    /*計算兩個緩沖區的按位異或*/
    for(i=0;i<size;i++)
    {
        if(bit_get(bits1,i) != bit_get(bits2,i))
            bit_set(bitsx,i,1);
        else
            bit_set(bitsx,i,0);
    }
    return;
}
/*bit_rot_left 輪轉緩沖區bits(含size位),將位值向左移count位*/
void bit_rot_left(unsigned char *bits,int size,int count)
{
    int  fbit,lbit,i,j;

    /*將緩沖區向左輪轉指定位數*/
    if(size > 0)
    {
        for(j=0; j<count; j++)
        {
            for(i=0; i<=((size-1)/8); i++)
            {
                /*獲得要從當前字節偏移的位*/
                lbit = bit_get(&bit[i],0);
                if(i==0)
                {
                    /*保存要從首字節移動到后面的位*/
                    fbit = lbit;
                }
                else
                {
                    /*將前一字節最右邊的位設置為當前字節最左邊的位*/
                    bit_set(&bits[i-1],7,lbit);
                }

                /*將當前字節向左移動*/
                bits[i] = bits[i] << 1;
            }
            /*將緩沖區最右邊的位設置為從第一個字節偏移的位*/
            bit_set(bits,size-1,fbit);
        }
    }
    return;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM