1. 程式人生 > >《演算法之美》的一處錯誤

《演算法之美》的一處錯誤

今天開始拿到《演算法之美》一本書,翻看起來,前陣子在看Charles Petzold大神的《編碼-隱匿在計算機軟硬體背後的語言》,百看不厭,真的是一本非常有趣的書,覺得左飛哥翻譯的不錯,瞭解到他還寫了《演算法之美》一書,特地買來看看。看到Z字形編碼,對其產生了興趣,於是開始敲程式碼。

  • Z字形編碼是幹什麼的? 

首先了解一下 JPEG ,它的英文名是 Joint Photographic Experts Group 是一種常見的影象檔案格式,也是目前靜態影象中壓縮比最高的一種影象檔案格式,它綜合運用了多種壓縮技術而達到一種極高的壓縮比例。

關於 JPEG 更多詳情 請見 維基百科

---https://zh.wikipedia.org/wiki/JPEG

在 JPEF 編碼過程中,有一個非常重要的步驟,即Z字形編排過程。

 

借用一下本書的一副圖,簡單說明一下Z字形編碼。

下面看一個例子,相信大家就明白了。

上面是 8 × 8 的一個矩陣,將其經過 Z 字形編碼得到的結果如果上圖。在 《演算法之美》這本書中,給的矩陣大小為 8 ,矩陣大小定義為 SIZE ,在本小節末尾,有這麼一句話 --- 此外,這個演算法不僅對 8 × 8 的矩陣有效,對於 SIZE 取其他值的情況仍然有效,讀者不妨試試看。

於是,我就試了將 SIZE 改為其它值,但是 SIZE為偶數時是沒有錯誤的,但是 SIZE 一旦為奇數時就發生了錯誤。

錯誤情況如圖所示:

 

也就是說沒有完成 Z 字形編碼,所以我就想把 SIZE 為奇數的情況,也能夠進行正確的 Z 字形編碼。

經過一番在草稿紙上的演算,只需將書中的兩行程式碼做下小小的改動再加上兩個判斷即可。

程式碼如下:

  • SIZE 為奇數的情況:
		if(SIZE%2 == 1)
		{
			if(i == 0 && j%2 == 0 && j !=SIZE-1 || i == SIZE -1 && j%2 == 1)
			{
				j++;
				continue;
			}
			
			if(j == 0 && i%2 == 1 || j == SIZE - 1 && i%2 == 0) 
			{
				i++;
				continue;
			}
		}
  •  SIZE 為偶數的情況:
		else if(SIZE%2 == 0)
		{
			if((i == 0 || i == SIZE-1) && j%2 == 0)
			{
				j++;
				continue;
			}
			
			if((j == 0 || j == SIZE-1) && i%2 == 1)
			{
				i++;
				continue;
			}	
		}

完整的判斷程式碼為:

for(x = 0;x< SIZE;x++)
	{
		for(y = 0;y< SIZE;y++)	
		{
			//賦值
			*(*(a + i) + j) = *(*(matrix + x) +y);
		if(SIZE%2 == 1)
		{
			if(i == 0 && j%2 == 0 && j !=SIZE-1 || i == SIZE -1 && j%2 == 1)
			{
				j++;
				continue;
			}
			
			if(j == 0 && i%2 == 1 || j == SIZE - 1 && i%2 == 0) 
			{
				i++;
				continue;
			}
		}
		else if(SIZE%2 == 0)
		{
			if((i == 0 || i == SIZE-1) && j%2 == 0)
			{
				j++;
				continue;
			}
			
			if((j == 0 || j == SIZE-1) && i%2 == 1)
			{
				i++;
				continue;
			}	
		}
			
			if((i+j)%2 == 0)
			{
				i--;
				j++;
			}
			
			else if((i+j)%2 == 1)
			{
				i++;
				j--;
			}
			
		}
	}

 

SIZE 為7時 :

下面來分別解釋一下 SIZE 為偶數 和 奇數的情況:(也就是程式碼怎麼來的)

 

 

上圖其實也有一處錯誤,只不過這個錯誤沒有什麼太大的影響所以就權當沒看到了。

 

 以7 × 7 的矩陣為例:

  • 當 SIZE 為偶數時:
  • 當 SIZE 為奇數時:

 可以發現:

  1. 當 matrix[i][j] 列數 j 是偶數時,並且只有當 i = 0 時,那麼遍歷路徑在矩陣中的走向就是水平向右移動一格。
  2. 當 matrix[i][j] 列數 j 是奇數時,並且只有當 i == SIZE -1 時,那麼遍歷路徑在矩陣中的走向就是水平向右移動一格。
  3. 當 matrix[i][j] 行數 i 是奇數時,並且只有當 j = 0 時,那麼遍歷路徑在矩陣中的走向就是垂直向下移動一格。
  4. 當 matrix[i][j] 行數 i 是偶數時,並且只有當 j == SIZE -1 時,那麼遍歷路徑在矩陣中的走向就是垂直向下移動一格。

以上就是 完整的 Z字形編碼 過程。

 附完整程式碼:

#include <iostream>
#include <iomanip>
using namespace std;
#define SIZE 3
int main(int arc,char** argv)
{
	int matrix[SIZE][SIZE] = {0};
	int a[SIZE][SIZE] = {0};
	
	int i,j,x,y,value = 0;
	
	int *p;
	p = &matrix[0][0];
	
	//初始化矩陣
	for(i = 0;i<SIZE * SIZE;i++)
		*p++ = i; 
		
	//列印原始矩陣
	cout << "原始矩陣如下:" << endl; 
	for(i = 0; i< SIZE ; i++)
	{
		for(j = 0;j<SIZE;j++)
			//setw(int n) 用來控制輸出間隔 
			cout << setw(4) << *(*(matrix + i) + j);
		cout << endl; 
	}
	
	i = 0; j = 0;
	//進行Z字形編排
	for(x = 0;x< SIZE;x++)
	{
		for(y = 0;y< SIZE;y++)	
		{
			//賦值
			*(*(a + i) + j) = *(*(matrix + x) +y);
		if(SIZE%2 == 1)
		{
			if(i == 0 && j%2 == 0 && j !=SIZE-1 || i == SIZE -1 && j%2 == 1)
			{
				j++;
				continue;
			}
			
			if(j == 0 && i%2 == 1 || j == SIZE - 1 && i%2 == 0) 
			{
				i++;
				continue;
			}
		}
		else if(SIZE%2 == 0)
		{
			if((i == 0 || i == SIZE-1) && j%2 == 0)
			{
				j++;
				continue;
			}
			
			if((j == 0 || j == SIZE-1) && i%2 == 1)
			{
				i++;
				continue;
			}	
		}
			
			if((i+j)%2 == 0)
			{
				i--;
				j++;
			}
			
			else if((i+j)%2 == 1)
			{
				i++;
				j--;
			}
			
		}
	}
	cout << endl << "經過Z字形編碼後的矩陣如下:" << endl; 
	for(i = 0;i< SIZE;i++)
	{
		for(j = 0;j<SIZE;j++)
			cout << setw(4)<<*(*(a+i)+j);
		cout<<endl;
	}
	return 0;
}