信息論與編碼課程設計


信息論課程設計

算術編碼的編碼與譯碼:

要求:輸入字符集為{a,b},且p(a)=1/4,p(b)=3/4.對長度L<=50的序列進行算術編碼,並進行反向譯碼

輸入文件:in1.txt,含至少兩組輸入,每組為滿足要求的串

輸出文件:out1.txt,對每組輸入的編碼和譯碼結果

問題分析和實現原理

編碼原理
算數編碼的原理我個人感覺其實並不太容易用三言兩語直觀地表達出來,其背后的數學思想則更是深刻。當然在這里我還是盡可能地將它表述,並着重結合例子來詳細講解它的原理。

簡單來說,算數編碼做了這樣一件事情:

  1. 假設有一段數據需要編碼,統計里面所有的字符和出現的次數。

  2. 將區間 [0,1) 連續划分成多個子區間,每個子區間代表一個上述字符, 區間的大小正比於這個字符在文中出現的概率 p。概率越大,則區間越大。所有的子區間加起來正好是 [0,1)。

  3. 編碼從一個初始區間 [0,1) 開始,設置:
    low=0, high=1

  4. 不斷讀入原始數據的字符,找到這個字符所在的區間,比如 [ L, H ),更新:

    low=low+(high-low)\*L high=low+(high-low)\*H

最后將得到的區間 [low, high)中任意一個小數以二進制形式輸出即得到編碼的數據。
乍一看這些數學和公式很難給人直觀理解,所以我們還是看例子。例如有一段非常簡單的原始數據:

ARBER
統計它們出現的次數和概率:

Symbol Times P
A 1 0.2
B 1 0.2
E 1 0.2
R 2 0.4

將這幾個字符的區間在 [0,1) 上按照概率大小連續一字排開,我們得到一個划分好的 [0,1)區間:

開始編碼,初始區間是 [0,1)。注意這里又用了區間這個詞,不過這個區間不同於上面代表各個字符的概率區間 [0,1)。這里我們可以稱之為編碼區間,這個區間是會變化的,確切來說是不斷變小。我們將編碼過程用下圖完整地表示出來:

拆解開來一步一步看:

  • 剛開始編碼區間是 [0,1),即 low=0 high=1

  • 第一個字符A的概率區間是 [0,0.2),則 L = 0,H = 0.2,更新
    low=low+(high-low)\*L=0 high=low+(high-low)*H=0.2

  • 第二個字符R的概率區間是 [0.6,1),則 L = 0.6,H = 1,更新
    low=low+(high-low)\*L=0.12 high=low+(high-low)*H=0.2

  • 第三個字符B的概率區間是 [0.2,0.4),則 L = 0.2,H = 0.4,更新
    low=low+(high-low)\*L=0.136 high=low+(high-low)*H=0.152

  • ......

    上面的圖已經非常清楚地展現了算數編碼的思想,我們可以看到一個不斷變化的小數編碼區間。每次編碼一個字符,就在現有的編碼區間上,按照概率比例取出這個字符對應的子區間。例如一開始A落在0到0.2上,因此編碼區間縮小為 [0,0.2),第二個字符是R,則在 [0,0.2)上按比例取出R對應的子區間 [0.12,0.2),以此類推。每次得到的新的區間都能精確無誤地確定當前字符,並且保留了之前所有字符的信息,因為新的編碼區間永遠是在之前的子區間。最后我們會得到一個長長的小數,這個小數即神奇地包含了所有的原始數據,不得不說這真是一種非常精彩的思想。

解碼
如果你理解了編碼的原理,則解碼的方法顯而易見,就是編碼過程的逆推。從編碼得到的小數開始,不斷地尋找小數落在了哪個概率區間,就能將原來的字符一個個地找出來。例如得到的小數是0.14432,則第一個字符顯然是A,因為它落在了 [0,0.2)上,接下來再看0.14432落在了 [0,0.2)區間的哪一個相對子區間,發現是 [0.6,1), 就能找到第二個字符是R,依此類推。在這里就不贅述解碼的具體步驟了。

編程實現
算數編碼的原理簡潔而又精致,理解起來也不很困難,但具體的編程實現其實並不是想象的那么容易,主要是因為小數的問題。雖然我們在講解原理時非常容易地不斷計算,但如果真的用編程實現,例如C++,並且不借助第三方數學庫,我們不可能簡單地用一個double類型去表示和計算這個小數,因為數據和編碼可以任意長,小數也會到達小數點后成千上萬位。

​ 怎么辦?其實也很容易,小數點是可以挪動的。給定一個編碼區間,例如從上面例子里最后的區間 [0.14432,0.1456)開始,假定還有新的數據進來要繼續編碼。現有區間小數點后的高位0.14其實是確定的,那么實際上14已經可以輸出了,小數點可以向后移動兩位,區間變成 [0.432,0.56),在這個區間上繼續計算后面的子區間。這樣編碼區間永遠保持在一個有限的精度要求上。

​ 上述是基於十進制的,實際數字是用二進制表示的,當然原理是一樣的,用十進制只是為了表述方便。

​ 算術編碼是把一個信源表示為實軸上0和1之間的一個區間,信源集合中的每一個元素都用來縮短這個區間。

算法流程:

  1. 輸入信源符號個數,信源概率分布,還有需要編碼的符號序列。

  2. 根據概率可以算出初始編碼間隔,

    High--當前編碼的上限,

    Low--當前編碼的下限,

    high--中間變量,用來計算下一個編碼符號的當前間隔的上限,

    low--中間變量,用來計算下一個編碼符號的當前間隔的下限,

    d--當前間隔之間的距離。

  3. 掃描需編碼的符號序列,確定編碼空間

    第1個編碼符號的當前間隔為其初始的編碼間隔,

    第i個編碼符號的當前間隔為第i-1個編碼后的[Low, High),

    第i+1個編碼符號的當前間隔算法如下: high=Low+d\*第i+1個初始編碼符號對應的上限 low=Low+d*第i+1個編碼符號對應的下限,然后High=high, Low=low, d=d\*第i個編碼符號的概率

    流程圖

實現源碼

#include<iostream>
#include<fstream>
#pragma warning(disable:4996)
using namespace std;
#include"math.h"
char S[100], A[10];//用戶的碼字與保存輸入字符
float P[10], f[10], gFs;//字符概率
char bm[100], jm[100]; //保存碼字
fstream in("in1.txt");
fstream out("out1.txt");
void bianma(int a, int h)//算術編碼
{
	int i, j;
	float fr;
	float ps = 1;
	float Fs = 0;
	float Sp[100];
	for (i =0; i < h; i++)//以待編碼的個數和字符個數為循環周期,將待編碼的字符所對應的概率存入到Fs中
	{
		for (j = 0; j < a; j++)
		{
			if (S[i] == A[j])
			{
				Sp[i] = P[j];
				fr = f[j];//將划分好的[0,1)區間的對應點賦值給fr
			}
		}
		Fs = Fs + ps * fr;
		ps *= Sp[i];
	}
	cout << "Fs=" << Fs << endl;
	gFs = Fs;
	out << "算術編碼結果:" << endl;
	out << "Fs=" << Fs << endl;
	float l = log((float)1 / ps) / log((float)2);//計算算術編碼的碼字長度l
	if (l > (int)l)l = (int)l + 1;
	else l = int(l);//將Fs轉換成二進制形式
	int d[20];
	int m = 0;
	while (l > m)
	{
		Fs = 2 * Fs;
		if (Fs > 1)
		{
			Fs = Fs - 1;
			d[m] = 1;
		}
		else if (Fs < 1)d[m] = 0;
		else { d[m] = 1; break; }
		m++;
	}
	int z = m;
	if (m >= l)//解決有關算術編碼的進位問題
	{
		while (1)
		{
			d[m - 1] = ( d[m - 1] + 1 ) % 2;
			if (d[m - 1] == 1)break;
			else m--;
		}
	}
	cout << "s=";
	out << "s=";
	for (int e = 0; e < z; e++)
	{
		cout << d[e];
		out << d[e];
	}
	cout << endl;
	out << endl;
}
void jiema(int a,int h)//算術譯碼
{
	int i;
	float Ft, Pt;
	float Fs = 0, Ps = 1;
	out << "算術譯碼后:";
	for (i = 0; i < h; i++)//以編碼個數和符號個數為循環周期進行解碼
	{
		for (int j = a - 1; j > -1; j--)
		{
			Ft = Fs;
			Pt = Ps;
			Ft += Pt * f[j];//對其進行逆編碼
			Pt *= P[j];
			if (gFs >= Ft)//對其進行判斷並將值存入到數組A中
			{
				Fs = Ft;
				Ps = Pt;
				cout << A[j];
				out << A[j];
				break;
			}
		}
	}
	cout << endl;
	out << endl;
}
void main()
{
	cout << "編碼個數";
	int a, i, h = 0;
	in >> a;
	cout << a << endl;
	cout << "輸入編碼符號和概率值" << endl;
	for (i = 0; i < a; i++)
	{
		char x;
		float y;
		in >> x;
		cout << x;
		A[i] = x;
		in >> y;
		cout << y << endl;
		P[i] = y;
	}
	for (i = 1; i < a; i++)
	{
		f[0] = 0;
		f[i] = f[i - 1] + P[i - 1];//將要編碼的數據映射到一個位於[0,1)的實數區間中
	}
	while (!in.eof())
	{
		cout << "輸入需要編碼的符號序列,同時用$結尾" << endl;
		h = 0;
		while (1)//將要編碼的字符存入到數組S中
		{
			char cc;
			in >> cc;
			if (cc == '$')
				break;//在以“$”為結尾的時候結束存儲
			S[h++] = cc;
			cout << cc;
		}
		cout << endl;
		cout << "算術編碼結果:" << endl;
		bianma(a, h);
		cout << "對應的解碼結果:" << endl;
		jiema(a, h);
		system("pause");
	}
	in.close();
	out.close();
}

運行結果

終端運行界面

文件夾中的in1.txt和out1.txt

設計體會

通過本次實驗,學會了算術編碼技基本流程,基本能夠調試算術編碼程序。對信息論相關知識有了進一步的了解。同時提高了自己解決問題的能力,對編程能力也有很大的提高。


判定唯一可譯碼:

輸入:任意的一個碼(即已知碼字個數及每個具體的碼字)

輸出:判決結果(是/不是)

輸入文件:in4.txt,含至少2組碼,每組的結尾為”$”符

輸出文件:out4.txt,對每組碼的判斷結果

說明:為了簡化設計,可以假定碼字為0,1串

問題分析和實現原理

根據薩德納斯和彼特森設計判斷唯一可以碼的算法思想,設計算法,判斷一個碼是否為唯一可譯碼

判斷標准:

將碼所有可能的未遂后綴組成一個集合,當且僅當集合中沒有包含任意碼字時,碼為唯一可譯碼。

實驗原理
1、考查C中所有的碼字,若Wi是Wj的前綴,則將相應的后綴作為一個尾隨后綴碼放入集合F0中;
2、考察C和Fi兩個集合,若Wi∈C是Wi∈Fi的前綴或Wi∈Fi是Wj∈C的前綴,則將相應的后綴作為尾隨后綴碼放入集合Fi+1中;
3、F= $ \cup $ Fi即為碼C的尾隨后綴集合;
4、若F中出現了C中的元素,算法終止,返回假(C不是唯一可譯碼);否則若F中沒有出現新的元素,則返回真。

流程圖

實現源碼

#include<iostream>
#include<fstream>
#include<stdio.h>
#include<string.h>
using namespace std;
#pragma warning(disable:4996)
char c[100][50];//保存碼字
char f[300][50];//保存尾隨后綴
int N, sum = 0;//分別為碼字的個數和尾隨后綴個數
int flag;//判斷是否為唯一可譯碼
void patterson(char c[], char d[])//檢測尾隨后綴
{
	int i, j, k;
	for (i = 0;; i++)
	{
		if (c[i] == '\0'&&d[i] == '\0')//兩字符串長度相同
			break;
		if (c[i] == '\0')//d比c長,將d的尾隨后綴放入到f中
		{
			for (j = i; d[j] != '\0'; j++)
				f[sum][j - i] = d[j];
			f[sum][j - i] = '\0';
			for (k = 0; k < sum; k++)
			{
				if (strcmp(f[sum], f[k]) == 0)//查看當前生成的尾隨后綴在f集合中是否存在
				{
					sum--;
					break;
				}
			}
			sum++;
			break;
		}
		if (d[i] == '\0')//c比d長,將c的尾隨后綴放入f中
		{
			for (j = i; c[j] != '\0'; j++)
				f[sum][j - i] = c[j];
			f[sum][j - i] = '\0';
			for (k = 0; k < sum; k++)
			{
				if (strcmp(f[sum], f[k]) == 0)//查看當前生成的尾隨后綴在f集合中是否存在
				{
					sum--;
					break;
				}
			}
			sum++;
			break;
		}
		if (c[i] != d[i])
			break;
	}
}
void main()
{
	int k = 0, N = 0, m = 0, a[50], z = 0;
	a[m] = N; m++;
	fstream file1;
	file1.open("out4.txt");
	FILE *file;
	file = fopen("in4.txt", "r+");//讀取碼字
	int num = fgetc(file) - 48;
	for (int n = 0; n < num; n++)
	{
		int i = 0, j;
		if (fgetc(file) == ' ')
			N += (fgetc(file) - 48);
		else
			N += (fgetc(file) - 48);
		a[m] = N; m++;
		fgetc(file);
		for (k; k < N; k++)
		{
			for (int q = 0;; q++)
			{
				char temp = fgetc(file);
				c[k][q] = temp;
				if (temp == ' ' || temp == '$')
				{
					c[k][q] = '\0';
					break;
				}
			}
		}
		flag = 0;
		for (i = a[z]; i < N - 1; i++)//判斷碼本身是否重復
			for (j = i + 1; j < N; j++)
			{
				if (strcmp(c[i], c[j]) == 0)
				{
					flag = 1; break;
				}
			}
		if (flag == 1)//如果碼本身存在重復,即可斷定其不是唯一可譯碼
		{
			for (int y = a[z]; y < N; y++)
				file1 << c[y] << ' ';
			file1 << "不是唯一可譯碼\n";
		}
		else {
			for (i = a[z]; i < N - 1; i++)//將原始碼字生成的尾隨后綴集合s[1]放入f中
			{
				for (j = i + 1; j < N; j++)
				{
					patterson(c[i], c[j]);
				}
			}
			for (i = 0;; i++)//根據原始碼與s[i]生成s[i+1]也放入f[i]
			{
				int s = 0;
				for (j = a[z]; j < N; j++)//判斷s[i+1]中的字符串是否與s[i]中一樣,重復則不再添加
				{
					if (i == sum)
					{
						s = 1; break;
					}
					else
						patterson(f[i], c[j]);
				}
				if (s == 1)
					break;
			}
			for (i = 0; i < sum; i++)//判斷尾隨后綴與原始碼字是否相同,相同則不是唯一可譯碼
			{
				for (j = a[z]; j < N; j++)
				{
					if (strcmp(f[i], c[j]) == 0)
					{
						flag = 1;
						break;
					}
				}
			}
			if (flag == 1)
			{
				for (int y = a[z]; y < N; y++)
					file1 << c[y] << ' ';
				file1 << "不是唯一可譯碼\n";
			}
			else {
				for (int y = a[z]; y < N; y++)
					file1 << c[y] << ' ';
				file1 << "是唯一可譯碼\n";
			}
		}
		file1 << "尾隨后綴集合為:";
		for (i = 0; i < sum; i++)
			file1 << f[i] << ' ';
		file1 << "\n";
		z++;
		sum = 0;
	}
}

運行結果

設計體會

通過對判定唯一可譯碼算法的實現,我進一步了解了判定唯一可譯碼縮的基本原理及過程,體會到了其重要性,同時也鍛煉了我獨立分析問題以及解決問題的能力,這次課程設計讓我深刻認識到了自己編程能力的不足,在以后的學習中要加強自己的編程能力的提高。


綜合編碼:

游程編碼+Huffman編碼+加密編碼+信道編碼及對應的譯碼(L-D+Huffman編碼+加密編碼+信道編碼及對應的譯碼)

一個二元無記憶信源,0符號的出現概率為1/4, 1符號的出現概率為3/4。

現要求對該信源連續出現的n個符號序列,進行游程編碼/對游程編碼結果進行Huffman編碼/使用加密算法進行加密/進行信道編碼;

然后模擬信道的傳輸過程,並對收到的信息串進行信道譯碼/解密譯碼/Huffman譯碼/游程譯碼。

假定,連續出現的0或1序列的長度不超過16,n不小於256。

其中加密編碼/解密譯碼可自主選擇對稱加密算法或非對稱算法;

信道編碼要求采用(7,4)系統循環碼,其中,g(x)= \(X^3\)+\(x\)+1,譯碼采用簡化的譯碼表;

信道為BSC信道,p=\(10^{-6}\)

輸入:

長為n的0/1串

輸出:

  1. 游程編碼結果,

  2. Huffman編碼結果,

  3. 加密編碼結果

  4. 信道編碼結果

  5. 模擬接收串,

  6. 信道譯碼結果,

  7. 解密編碼結果 ,

  8. Huffman譯碼結果

  9. 游程譯碼結果

輸入文件:in5.txt,含至少兩組輸入

輸出文件:out5.txt,對每組輸入的處理結果

問題分析和實現原理

游程編碼

  • 游程編碼是一種比較簡單的壓縮算法,其基本思想是將重復且連續出現多次的字符使用(連續出現次數,某個字符)來描述。

    比如一個字符串:

    AAAAABBBBCCC

    使用游程編碼可以將其描述為:

    5A4B3C

    5A表示這個地方有5個連續的A,同理4B表示有4個連續的B,3C表示有3個連續的C,其它情況以此類推。

    在對圖像數據進行編碼時,沿一定方向排列的具有相同灰度值的像素可看成是連續符號,用字串代替這些連續符號,可大幅度減少數據量。

    游程編碼記錄方式有兩種:①逐行記錄每個游程的終點列號:②逐行記錄每個游程的長度(像元數)

Huffman編碼

  • 霍夫曼編碼是一種無損的統計編碼方法,利用信息符號概率分布特性來改編字長進行編碼。適用於多元獨立信源。霍夫曼編碼對於出現概率大的信息符號用字長小的符號表示,對於出現概率小的信息用字長大的符號代替。如果碼字長嚴格按照所對應符號出現的概率大小逆序排列,則編碼結果的平均字長一定小於其他排列形式。

    霍夫曼編碼的具體步驟如下:

    1)將信源符號的概率按減小的順序排隊。

    2)把兩個最小的概率相加,並繼續這一步驟,始終將較高的概率分支放在右邊,直到最后變成概率。

    3)畫出由概率1處到每個信源符號的路徑,順序記下沿路徑的0和1,所得就是該符號的霍夫曼碼字。

    4)將每對組合的左邊一個指定為0,右邊一個指定為1(或相反)。

    對稱加密

    對稱加密算法 是應用較早的加密算法,又稱為 共享密鑰加密算法。在 對稱加密算法 中,使用的密鑰只有一個,發送 和 接收 雙方都使用這個密鑰對數據進行 加密 和 解密。這就要求加密和解密方事先都必須知道加密的密鑰。

    數據加密過程:在對稱加密算法中,數據發送方 將 明文 (原始數據) 和 加密密鑰 一起經過特殊 加密處理,生成復雜的 加密密文 進行發送。

    數據解密過程:數據接收方 收到密文后,若想讀取原數據,則需要使用 加密使用的密鑰 及相同算法的 逆算法 對加密的密文進行解密,才能使其恢復成 可讀明文。

    信道編碼

    編碼過程

    ​ 在編碼時,首先需要根據給定循環碼的參數確定生成多項式g(x),也就是從F(x)的因子中選一個(n-k)次多項式作為g(x);然后,利用循環碼的編碼特點,即所有循環碼多項式A(x)都可以被g(x)整除,來定義生成多項式g(x)。
    據上述原理可以得到一個較簡單的系統:設要產生(n,k)循環碼,m(x)表示信息多項式,循環碼編碼方法,則其次數必小於k,而m(x)的次數必小於n,用m(x)除以g(x),可得余數r(x),r(x)的次數必小於(n-k),將r(x)加到信息位后作監督位,就得到了系統循環碼。下面就將以上各步處理加以解釋。
    (1)用x的乘m(x)。這一運算實際上是把信息碼后附加上(n-k)個“0”。例如,信息碼為110,而希望的到得系統循環碼多項式應當是A(x) = \(x^n\)·m(x) + r(x)。
    (2)求r(x)。由於循環碼多項式A(x)都可以被g(x)整除,因此,用·m(x)除以g(x),就得到商Q(x)和余式r(x),這樣就得到了r(x)。
    (3)編碼輸出系統循環碼多項式A(x)為:這時的編碼輸出為:1100101。
    上述三步編碼過程,在硬件實現時,可以利用除法電路來實現,這里的除法電路采用一些移位寄存器和模2加法器來構成。下面將以(7,4)循環碼為例,來說明其具體實現過程。設該(7,4)循環碼的生成多項式為:g(x)= \(x^3\)+\(x\)+1,則構成的系統循環碼編碼器如圖8-6所示,圖中有4個移位寄存器,一個雙刀雙擲開關。

    ​ 當信息位輸入時,開關位置接“2”,輸入的信息碼一方面送到除法器進行運算,一方面直接輸出;當信息位全部輸出后,開關位置接“1”,這時輸出端接到移位寄存器的輸出,這時除法的余項,也就是監督位依次輸出。
    順便指出,由於數字信號處理器(DSP)和大規模可編程邏輯器件(CPLD和FPGA)的廣泛應用,目前已多采用這些先進器件和相應的軟件來實現上述編碼。

    譯碼過程
    對於接收端譯碼的要求通常有兩個:檢錯與糾錯。達到檢錯目的的譯碼十分簡單通過判斷接收到的碼組多項式B(x)是否能被生成多項式g(x)整除作為依據。當傳輸中未發生錯誤時,也就是接收的碼組與發送的碼組相同,即A(x)=B(x),則接收的碼組B(x)必能被g(x)整除;若傳輸中發生了錯誤,則A(x)≠B(x),B(x)不能被g(x)整除。因此,可以根據余項是否為零來判斷碼組中有無錯碼。
    需要指出的是,有錯碼的接收碼組也有可能被g(x)整除,這時的錯碼就不能檢出了。這種錯誤被稱為不可檢錯誤,不可檢錯誤中的錯碼數必將超過這種編碼的檢錯能力。
    在接收端為糾錯而采用的譯碼方法自然比檢錯要復雜許多,因此,對糾錯碼的研究大都集中在譯碼算法上。
    我們知道,校正子與錯誤圖樣之間存在某種對應關系。如同其它線性分組碼,循環編碼和譯碼可以分三步進行:

    (1)由接收到的碼多項式B(x)計算校正子(伴隨式)多項式S(x);

    (2)由校正子S(x)確定錯誤圖樣E(x);

    (3)將錯誤圖樣E(x)與B(x)相加,糾正錯誤。

    ​ 上述第(1)步運算和檢錯譯碼類似,也就是求解B(x)整除g(x)的余式,第(3)步也很簡單。因此,糾錯碼譯碼器的復雜性主要取決於譯碼過程的第(2)步。

    ​ 基於錯誤圖樣識別的譯碼器稱為梅吉特譯碼器,它的原理圖如圖8-7所示。錯誤圖樣識別器是一個具有(n-k)個輸入端的邏輯電路,原則上可以采用查表的方法,根據校正子找到錯誤圖樣,利用循環碼的上述特性可以簡化識別電路。梅吉特譯碼器特別適合於糾正2個以下的隨機獨立錯誤。

    ​ k級緩存器用於存儲系統循環碼的信息碼元,模2加電路用於糾正錯誤。當校正子為0時,模2加來自錯誤圖樣識別電路的輸入端為0,輸出緩存器的內容;當校正子不為0時,模2加來自錯誤圖樣識別電路的輸入端在第i位輸出為1,它可以使緩存器輸出取補,即糾正錯誤。

    ​ 循環碼的譯碼方法除了梅吉特譯碼器以外,還有補錯編譯碼、大數邏輯編譯碼等方法。捕錯譯碼是梅吉特譯碼的一種變形,也可以用較簡單的組合邏輯電路實現,它特別適合於糾正突發錯誤、單個隨機錯誤和兩個錯誤的碼字。大數

    ​ 邏輯譯碼也稱為門限譯碼,這種譯碼方法也很簡單,但它只能用於有一定結構的為數不多的大數邏輯可譯碼,雖然在一般情形下,大數邏輯可譯碼的糾錯能力和編碼效率比有相同參數的其它循環碼(如BCH碼)稍差,但它的譯碼算法和硬件比較簡單,因此在實際中有較廣泛的應用。

流程圖

​ 綜合編碼流程圖

​ 游程壓縮實現流程圖

實現源碼

//游程編碼
void YCencode(char* str)
{
	int count = 1;
	int j = 0;
	int i = 0;
	if (str[0] == '1') { yc[0] = 0; j = 1; }
	for (i = 0; str[i] != 0; i++)
	{
		if (str[i + 1] == str[i])
		{
			count++;
		}
		else {
			yc[j] = count; j++;
			count = 1;
		}
	}
	yc[j] = 0;
	l = j;
	for (int k = 0; k < l; k++)
	{
		cout << yc[k] << " ";
	}
}


//對稱加密
string DCencode(string m, int l)
{
	string so;
	string temp;
	int c;
	int num(0), num1(0);
	bool b(false);
	for (int n(0); n < l; n++, num++)
	{
		if (num1 >= 3)
			num1 = 0;
		b = false;
		if (m[n] == ' ')
		{
			if (num == 0)
			{
				num = -1;
				b = true;
			}
			else
			{
				for (; num < 4; num++)
				{
					temp += '0';
				}
				b = true;
			}
		}
		else
		{
			temp += m[n];
		}
		if (num >= 3)
		{
			num = -1;
			c = bit_int(temp, 9);
			temp = "";
			so += int_bit(c);
			num1++;
		}
		if (b)
		{
			for (; num1 < 3; num1++)
			{
				so += int_bit(bit_int("0000", 9));//int_bit(((bit_int("0000") + 3) % 16));
			}
		}
	}
	return so;
}


//對稱解密
string DCdecode(string m, int l)
{
	string so;
	string temp, temp_0;
	int l1(0);
	int l2(0);
	for (int n(0); n < l; n++)
	{
		if (n % 4 == 3)
		{
			temp_0 += m[n];
			temp += int_bit(bit_int(temp_0, 7));
			temp_0 = "";
		}
		else
		{
			temp_0 += m[n];
		}
		if (n % 12 == 11)
		{
			l2 = l2 % 2;
			l1 = len1(temp, temp.length());
			for (int a(0); a < 17; a++)
				if (temp.compare(0, (l1 > (int)mh[a][l2].length() ? l1 : mh[a][l2].length()), mh[a][l2]) == 0)
				{
					so += mh[a][l2] + ' ';
					break;
				}
			temp = "";
			l2++;
		}
	}
	return so;
}



//信道編碼
string XDencode(string m, int l)
{
	string so;
	string temp;
	for (int n(0); n < l; n++)
	{
		temp += m[n];
		if (n % 4 == 3)
		{
			for (int n2(0); n2 < 16; n2++)
			{
				if (xd[n2].compare(0, 4, temp) == 0)
					so += xd[n2];
			}
			temp = "";
		}
	}
	return so;
}

//信道解碼
string XDdecode(string m, int l)
{
	string so, temp;
	bool b(false);
	for (int n(0); n < l; n++)
	{
		int n4;
		temp += m[n];
		if (n % 7 == 6)
		{
			cout << temp;
			n4 = 0;
			for (int n2(0); n2 < 16; n2++)
				if (temp.compare(xd[n2]) != 0)
				{
					n4++;
				}
				else
					break;
			if (n4 == 15)
			{
				temp = findxd(temp);
			}
			for (int n3(0); n3 < 4; n3++)
				so += temp[n3];
			temp = "";
		}
	}
	cout << endl;
	return so;
}


//模擬信道傳輸
int  suiji()   //產生隨機數
{
	srand((int)time(0));
	int a = rand() % 10;
	if (a == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

運行結果

in5.txt和out5.txt文件

設計體會

通過本次實驗,了解了霍夫曼編碼,游程編碼,加密編碼以及信道編碼的原理和基本流程,了解了編碼與譯碼,加密與解密的基本原理,實驗過程中通過查閱資料了解了RSA加密算法。經過本次實驗提高了我的解決問題的能力、查閱資料和文獻檢索的能力。


免責聲明!

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



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