JPEG影象壓縮解壓演算法——C++實現
阿新 • • 發佈:2019-01-29
/** * 作者:戴文治 * 時間:2017年11月17日 * 描述:JPEG壓縮與解壓演算法 * 測試環境:Dev-C++ 5.9.2 */ #include<iostream> #include<string> #include<cstdlib> #include<cstdio> #include<cmath> #define MAX 100 #define N 8 //N為每個影象分量的矩陣大小 using namespace std; /*亮度量化值表*/ struct BrightnessQuantizedValueTable{ int Q[N][N]; BrightnessQuantizedValueTable(){ int x[N][N]={16,11,10,16,24,40,51,61, 12,12,14,19,26,58,60,55, 14,13,16,24,40,57,69,56, 14,17,22,29,51,87,80,62, 18,22,37,56,68,109,103,77, 24,35,55,64,81,104,113,92, 49,64,78,87,103,121,120,101, 72,92,95,98,112,100,103,99}; for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ Q[i][j] = x[i][j]; } } } }; BrightnessQuantizedValueTable brightnessQuantizedValueTable;//定義一個亮度量化值表 /*亮度DC差值碼錶*/ struct BrightnessDC_DifferenceTableList{ string brightnessDC_DifferenceTable[12];//亮度DC差值碼錶 類別(陣列下標)與碼字對映 BrightnessDC_DifferenceTableList(){ brightnessDC_DifferenceTable[0]="00"; brightnessDC_DifferenceTable[1]="010"; brightnessDC_DifferenceTable[2]="011"; brightnessDC_DifferenceTable[3]="100"; brightnessDC_DifferenceTable[4]="101"; brightnessDC_DifferenceTable[5]="110"; brightnessDC_DifferenceTable[6]="1110"; brightnessDC_DifferenceTable[7]="11110"; brightnessDC_DifferenceTable[8]="111110"; brightnessDC_DifferenceTable[9]="1111110"; brightnessDC_DifferenceTable[10]="11111110"; brightnessDC_DifferenceTable[11]="111111110"; } }; BrightnessDC_DifferenceTableList brightnessDC_DifferenceTableList;//定義一個亮度DC差值碼錶 /*AC係數熵編碼時的中間符號*/ struct AC_EntropyCoding_MiddleSymbol{ string R_S; int temp; }; AC_EntropyCoding_MiddleSymbol ac_EntropyCoding_MiddleSymbol[N*N];//由於用函式返回結構體陣列,裡面的字串會出現一些無法處理的亂碼,故定義為全域性變數 /*熵編碼時的編碼輸出*/ struct EntropyCoding{ string strTemp1; string strTemp; }; EntropyCoding ac_EntropyCodingStr[N*N];//由於用函式返回結構體陣列,裡面的字串會出現一些無法處理的亂碼,故定義為全域性變數 /* R/S與碼字對映結點*/ struct StringMap{ string key; string value; }; /*亮度AC碼錶 R/S與碼字對映表*/ /**特別注意,以下的亮度AC碼錶只適用於給出的測試資料,如要其它的測試資料,則必須補全亮度AC碼錶**/ //網上找到一個fantasy 的部落格http://menmory.blog.163.com/blog/static/12690012620114535032530/ 這裡面有比較詳細的亮度AC碼錶等 struct StringMapList{ StringMap stringMap[N*N]; int partNum; //該亮度AC碼錶中的條數 StringMapList(){ //部分常用亮度AC碼錶 stringMap[0].key = "0/0(EOB)"; stringMap[0].value = "1010"; stringMap[1].key = "0/1"; stringMap[1].value = "00"; stringMap[2].key = "1/1"; stringMap[2].value = "1100"; stringMap[3].key = "1/2"; stringMap[3].value = "11011"; stringMap[4].key = "2/1"; stringMap[4].value = "11100"; stringMap[5].key = "3/2"; stringMap[5].value = "111110111"; stringMap[6].key = "F/0(ZRL)"; stringMap[6].value = "11111111001"; stringMap[7].key = "F/F"; stringMap[7].value = "1111111111111110"; partNum = 8; } }; StringMapList stringMapList;//定義一個 部分常用亮度AC碼錶 /*DC差值範圍表,本人通過對錶找規律發現如下規律 */ int DC_Difference(int temp){ int temp1; if(temp == 0){ temp1 = 0; }else{ for(int i=1;i<=11;i++){ if(abs(temp)<pow(2,i)){ temp1 = i; break; } } } return temp1; } /*AC係數範圍表,本人通過對錶找規律發現如下規律*/ int AC_Difference(int temp){ int temp1; if(temp == 0){ temp1 = 0; }else{ for(int i=1;i<=10;i++){ if(abs(temp)<pow(2,i)){ temp1 = i; break; } } } return temp1; } /*將正整數十進位制轉換成二進位制*/ string TenToTwo(int temp){ string strTemp=""; //舊方法 // for(int k=abs(temp);k>0;k=k/2){ // strTemp = strTemp + (k%2==1?'1':'0'); // } // //倒置 // int len = strTemp.length(); // for(int k=0;k<len/2;k++){ // char t = strTemp[k]; // strTemp[k] = strTemp[len-1-k]; // strTemp[len-1-k] = t; // } //新方法 char str[N*N]; itoa(temp,str,2); strTemp = str; return strTemp; } /*將正整數二進位制轉換成十進位制*/ int TwoToTen(string strTemp){ int temp=0; for(int i=0;i<strTemp.length();i++){ temp = temp*2+strTemp[i]-'0'; } return temp; } /*將一個負數的二進位制串逐位取反*/ string ConvertToComplement(string strTemp){ string str = ""; for(int i=0;i<strTemp.length();i++){ str = str + (strTemp[i]=='1'?'0':'1'); } return str; } /*DC係數編碼*/ EntropyCoding DC_EntropyCoding(int &temp,int &temp1){ //對DC係數生成中間符號(中間符號(temp1,temp)) //查DC差值表 temp1 = DC_Difference(temp); /*測試*/ // cout<<temp1<<endl; //對中間符號通過查表進行符號編碼 //對 temp1通過查亮度DC差值碼錶進行熵編碼 EntropyCoding dc_EntropyCodingStr; dc_EntropyCodingStr.strTemp1 = brightnessDC_DifferenceTableList.brightnessDC_DifferenceTable[temp1]; //對 temp進行轉換成補碼 //先將 temp轉換成二進位制串 dc_EntropyCodingStr.strTemp = TenToTwo(abs(temp)); //轉換成補碼 if(temp<0){ dc_EntropyCodingStr.strTemp = ConvertToComplement(dc_EntropyCodingStr.strTemp); } /*測試*/ // cout<<dc_EntropyCodingStr.strTemp1<<"\t"<<dc_EntropyCodingStr.strTemp<<endl; return dc_EntropyCodingStr; } /*AC係數編碼*/ bool AC_EntropyCoding(int F_[N][N],int &index){ //對AC係數生成中間符號中/後的部分 int SSSS[N][N]; //查AC係數範圍表,本人通過對錶找規律發現如下規律 for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ SSSS[i][j] = AC_Difference(F_[i][j]); } } /*測試*/ // for(int i=0;i<N;i++){ // for(int j=0;j<N;j++){ // cout<<SSSS[i][j]<<" "; // } // cout<<endl; // } //Z字形編碼 int count = 0;//計算0的個數 int i,j,t; for(i=0,j=1,t=1;t<=N-2;t++){//以下語句設為一個週期,大概要執行N-2個週期(這裡N=8,通過觀察發現每一下一上為一週期,則有6個週期+半段) //向左下方向 for(;i<N&&j>=0;i++,j--){ if(F_[i][j]==0){ count++; }else{ char countString[N*N]; itoa(count,countString,10);//將整數count轉換為字串並儲存在countString(以10進位制方式,也可指定2、8、10、16等進位制實現進位制轉換,進位制轉換新玩法) string strTemp = "/"; strTemp = countString + strTemp; //cout<<"--"<<strTemp<<"--"<<endl; char SSSS_String[N*N]; itoa(SSSS[i][j],SSSS_String,10); strTemp = strTemp + SSSS_String; //cout<<"**"<<strTemp<<"**"<<endl; //中間符號 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp; ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j]; index++; count = 0;//置為0 ,計算下個不為0的數前面0的個數 } } if(i>=N&&j<0){//當出現正中間往下時,挪回正規 i--; j = j+2; }else if(i>=N){//當出現往下突出時,挪回正規 i--; j = j+2; }else if(j<0){//當出現往左突出時,挪回正規 j++; } //向右上方向 for(;i>=0&&j<N;i--,j++){ if(F_[i][j]==0){ count++; }else{ char countString[N*N]; itoa(count,countString,10); string strTemp = "/"; strTemp = countString + strTemp; //cout<<"--"<<strTemp<<"--"<<endl; char SSSS_String[N*N]; itoa(SSSS[i][j],SSSS_String,10); strTemp = strTemp + SSSS_String; //cout<<"**"<<strTemp<<"**"<<endl; //中間符號 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp; ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j]; index++; count = 0;//置為0 ,計算下個不為0的數前面0的個數 } } if(i<0&&j>=N){//當出現正中間往上時,挪回正規 j--; i = i+2; }else if(i<0){//當出現往上突出時,挪回正規 i++; }else if(j>=N){//當出現往右突出時,挪回正規 j--; i = i+2; } } //剩下半個週期的編碼 //向左下方向 for(;i<N&&j>=0;i++,j--){ if(F_[i][j]==0){ count++; }else{ char countString[N*N]; itoa(count,countString,10); string strTemp = "/"; strTemp = countString + strTemp; //cout<<"--"<<strTemp<<"--"<<endl; char SSSS_String[N*N]; itoa(SSSS[i][j],SSSS_String,10); strTemp = strTemp + SSSS_String; //cout<<"**"<<strTemp<<"**"<<endl; //中間符號 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp; ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j]; index++; count = 0;//置為0 ,計算下個不為0的數前面0的個數 } } if(i>=N){//當出現往下突出時,挪回正規 i--; j = j+2; } if(F_[i][j]==0){//最後一個點 count++; ac_EntropyCoding_MiddleSymbol[index].R_S = "0/0(EOB)"; ac_EntropyCoding_MiddleSymbol[index].temp = INT_MAX; index++; }else{ char countString[N*N]; itoa(count,countString,10); string strTemp = "/"; strTemp = countString + strTemp; //cout<<"--"<<strTemp<<"--"<<endl; char SSSS_String[N*N]; itoa(SSSS[i][j],SSSS_String,10); strTemp = strTemp + SSSS_String; //cout<<"**"<<strTemp<<"**"<<endl; //中間符號 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp+"(EOB)"; ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j]; index++; } /*測試*/ // for(int k=0;k<index;k++){ // cout<<ac_EntropyCoding_MiddleSymbol[k].R_S<<"\t"<<ac_EntropyCoding_MiddleSymbol[k].temp<<endl; // } //對中間符號進行符號編碼 //對R/S通過查亮度AC碼錶進行熵編碼 for(int u=0;u<index;u++){ for(int v=0;v<stringMapList.partNum;v++){ if(ac_EntropyCoding_MiddleSymbol[u].R_S == stringMapList.stringMap[v].key){ ac_EntropyCodingStr[u].strTemp1 = stringMapList.stringMap[v].value; } } //對 temp進行轉換成補碼 //先將 temp轉換成二進位制串 if(ac_EntropyCoding_MiddleSymbol[u].R_S!="0/0(EOB)"){ ac_EntropyCodingStr[u].strTemp = TenToTwo(abs(ac_EntropyCoding_MiddleSymbol[u].temp)); //轉換成補碼 if(ac_EntropyCoding_MiddleSymbol[u].temp<0){ ac_EntropyCodingStr[u].strTemp = ConvertToComplement(ac_EntropyCodingStr[u].strTemp); } /*測試*/ // cout<<"**********"<<ac_EntropyCodingStr[u].strTemp<<endl; }else{ ac_EntropyCodingStr[u].strTemp = ""+'\0'; /*測試*/ // cout<<"**********+"<<ac_EntropyCodingStr[u].strTemp<<endl; } } /*測試*/ // for(int k=0;k<index;k++){ // cout<<ac_EntropyCodingStr[k].strTemp1<<" "<<ac_EntropyCodingStr[k].strTemp<<endl; // } return true; } int main(){ cout<<"壓縮編碼過程:"<<endl; /*壓縮編碼*/ const double PI = acos(-1); // double f[N][N]={139,144,149,153,155,155,155,155, // 144,151,153,156,159,156,156,156, // 150,155,160,163,158,156,156,156, // 159,161,162,160,160,159,159,159, // 159,160,161,162,162,155,155,155, // 161,161,161,161,160,157,157,157, // 162,162,161,163,162,157,157,157, // 162,162,161,161,163,158,158,158}; double f[N][N]; double ff[N][N],F[N][N]; /*輸入影象的一個分量樣本*/ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ cin>>f[i][j]; } } cout<<"源影象的一個分量樣本:"<<endl; /*輸出—源影象的一個分量樣本*/ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ cout<<f[i][j]<<"\t"; } cout<<endl; } //影象的一個分量樣本-128後 for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ ff[i][j] = f[i][j]-128; } } cout<<"源影象的一個分量樣本-128後:"<<endl; /*輸出—影象的一個分量樣本-128後*/ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ cout<<ff[i][j]<<"\t"; } cout<<endl; } //由公式計算DCT變化後的係數矩陣 for(int u=0;u<N;u++){ for(int v=0;v<N;v++){ double temp = 0.0; for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ temp = temp + ff[i][j]*cos((2*i+1)*u*PI*1.0/16)*cos((2*j+1)*v*PI*1.0/16); } } F[u][v] = 1.0/4*(u==0?1.0/sqrt(2):1)*(v==0?1.0/sqrt(2):1)*temp; } } /*輸出—DCT變化後的係數矩陣*/ //DCT變化後的係數矩陣 cout<<"DCT變化後的係數矩陣:"<<endl; for(int u=0;u<N;u++){ for(int v=0;v<N;v++){ printf("%.1f\t",F[u][v]); //cout<<F[u][v]<<" "; } cout<<endl; } //利用公式將DCT變化後的係數矩陣轉換為規格化量化係數矩陣 int F_[N][N];//規格化量化係數矩陣 for(int i=0;i<N;i++){ for(int j=0;j<N;j++){//二維陣列Q 為亮度量化值表 F_[i][j] = (int)((F[i][j]/brightnessQuantizedValueTable.Q[i][j])>0.0)?floor((F[i][j]/brightnessQuantizedValueTable.Q[i][j]) + 0.5) : ceil((F[i][j]/brightnessQuantizedValueTable.Q[i][j]) - 0.5);//進行量化,然後進行四捨五入 } } /*輸出—規格化量化係數矩陣*/ //規格化量化係數矩陣 cout<<"規格化量化係數:"<<endl; for(int u=0;u<N;u++){ for(int v=0;v<N;v++){ cout<<F_[u][v]<<"\t"; } cout<<endl; } //對DC係數生成中間符號(temp1,temp) int temp = F_[0][0]; int temp1; EntropyCoding dc_EntropyCodingStr = DC_EntropyCoding(temp,temp1); cout<<"中間符號:"<<endl; /*輸出—DC係數中間符號*/ cout<<temp1<<"\t"<<temp<<endl; int index=0; //AC係數生成中間符號的個數 //對AC係數生成中間符號 AC_EntropyCoding(F_,index); /*輸出—AC係數中間符號*/ for(int k=0;k<index;k++){ cout<<ac_EntropyCoding_MiddleSymbol[k].R_S<<"\t"; if(ac_EntropyCoding_MiddleSymbol[k].R_S!="0/0(EOB)"){ cout<<ac_EntropyCoding_MiddleSymbol[k].temp; } cout<<endl; } cout<<"熵編碼輸出:"<<endl; /*輸出—DC係數熵編碼輸出*/ cout<<dc_EntropyCodingStr.strTemp1<<"\t"<<dc_EntropyCodingStr.strTemp<<endl; /*輸出—AC係數熵編碼輸出*/ for(int k=0;k<index;k++){ cout<<ac_EntropyCodingStr[k].strTemp1<<"\t"<<ac_EntropyCodingStr[k].strTemp<<endl; } cout<<"----------------------------------------------------------------------"<<endl<<endl; cout<<"解碼過程:"<<endl; //下面的解碼過程只用到了壓縮過程傳輸過來的熵編碼和熵編碼中AC係數的個數index(也可以通過一個迴圈來計算出這個index) cout<<"待解碼的熵編碼:"<<endl; /*輸出—DC係數熵編碼輸出*/ cout<<dc_EntropyCodingStr.strTemp1<<"\t"<<dc_EntropyCodingStr.strTemp<<endl; /*輸出—AC係數熵編碼輸出*/ for(int k=0;k<index;k++){ cout<<ac_EntropyCodingStr[k].strTemp1<<"\t"<<ac_EntropyCodingStr[k].strTemp<<endl; } /*解碼*/ //將DC係數熵編碼的編碼轉換為中間符號 //對strTemp1進行反向查詢亮度DC差值碼錶得到temp1 int Itemp1; for(int i=0;i<11;i++){ if( dc_EntropyCodingStr.strTemp1 == brightnessDC_DifferenceTableList.brightnessDC_DifferenceTable[i]){ Itemp1 = i; } } //對strTemp進行反向補碼得到temp,可通過查詢規律發現若是負數轉換為補碼後首個數字必為0,正數必不為0 int Itemp; if(dc_EntropyCodingStr.strTemp[0]=='0'){//為負數,先取反 string tempStr = ConvertToComplement(dc_EntropyCodingStr.strTemp); //轉換為10進位制 Itemp = TwoToTen(tempStr); //加負號 Itemp = -Itemp; } else{//為正數,直接轉換為10進位制即可 Itemp = TwoToTen(dc_EntropyCodingStr.strTemp); } cout<<"中間符號:"<<endl; /*輸出—DC中間符號*/ cout<<Itemp1<<"\t"<<Itemp<<endl; //將AC係數熵編碼的編碼轉換為中間符號 AC_EntropyCoding_MiddleSymbol Iac_EntropyCoding_MiddleSymbol[N*N]; //遍歷所有的AC係數熵編碼的編碼對strTemp1進行反向查詢亮度AC碼錶得到R_S for(int i=0;i<index;i++){ for(int u=0;u<stringMapList.partNum;u++){ if(ac_EntropyCodingStr[i].strTemp1==stringMapList.stringMap[u].value){ Iac_EntropyCoding_MiddleSymbol[i].R_S = stringMapList.stringMap[u].key; } } //對strTemp進行反向補碼得到temp,可通過查詢規律發現若是負數轉換為補碼後首個數字必為0,正數必不為0 if(ac_EntropyCodingStr[i].strTemp[0]=='0'){//為負數,先取反 string tempStr = ConvertToComplement(ac_EntropyCodingStr[i].strTemp); //轉換為10進位制 Iac_EntropyCoding_MiddleSymbol[i].temp = TwoToTen(tempStr); //加負號 Iac_EntropyCoding_MiddleSymbol[i].temp = -Iac_EntropyCoding_MiddleSymbol[i].temp; } else{//為正數,直接轉換為10進位制即可 Iac_EntropyCoding_MiddleSymbol[i].temp = TwoToTen(ac_EntropyCodingStr[i].strTemp); } } /*輸出—AC中間符號*/ for(int i=0;i<index;i++){ cout<<Iac_EntropyCoding_MiddleSymbol[i].R_S<<"\t"; if(Iac_EntropyCoding_MiddleSymbol[i].R_S!="0/0(EOB)"){ cout<<Iac_EntropyCoding_MiddleSymbol[i].temp; } cout<<endl; } //規格化量化係數 int IF_[N][N]; //初始化矩陣 for(int u=0;u<N;u++){ for(int v=0;v<N;v++){ IF_[u][v]=0; } } //DC係數 //還原編碼 IF_[0][0] = Itemp; //AC係數 //Z字形還原編碼 int count;//計算0的個數 int a=0,b=1; //初始位置 for(int h=0;h<index;h++){ //將/前的字串轉換為整數 count = 0; for(int w=0;Iac_EntropyCoding_MiddleSymbol[h].R_S[w]!='/';w++){ count = count*10 + Iac_EntropyCoding_MiddleSymbol[h].R_S[w]-'0'; } /*測試*/ // cout<<"count:"<<count<<endl; while(Iac_EntropyCoding_MiddleSymbol[h].R_S!="0/0(EOB)"){ //根據查詢規律發現,當行+列為奇數時向左下方向,當行+列為偶數時為右上方向 if((a+b)%2==0){//偶數,向右上方向 for(;count>=0&&a>=0&&b<N;a--,b++){ if(count==0){//此時放temp IF_[a][b] = Iac_EntropyCoding_MiddleSymbol[h].temp; count--; break; }else{//此時放0 IF_[a][b] = 0; count--; } } if(count<0){//向右上移動 a--; b++; } if(a<0&&b>=N){//當出現正中間往上時,挪回正規 b--; a = a+2; }else if(a<0){//當出現往上突出時,挪回正規 a++; }else if(b>=N){//當出現往右突出時,挪回正規 b--; a = a+2; } if(count<0){//跳出到第一層迴圈 break; } } else{//奇數,向左下方向 for(;count>=0&&a<N&&b>=0;a++,b--){ if(count==0){//此時放temp IF_[a][b] = Iac_EntropyCoding_MiddleSymbol[h].temp; count--; break; }else{//此時放0 IF_[a][b] = 0; count--; } } if(count<0){//向左下移動 a++; b--; } if(a>=N&&b<0){//當出現正中間往下時,挪回正規 a--; b = b+2; }else if(a>=N){//當出現往下突出時,挪回正規 a--; b = b+2; }else if(b<0){//當出現往左突出時,挪回正規 b++; } if(count<0){//跳出到第一層迴圈 break; } } } } /*輸出—規格化量化係數矩陣*/ //規格化量化係數矩陣 cout<<"規格化量化係數:"<<endl; for(int u=0;u<N;u++){ for(int v=0;v<N;v++){ cout<<IF_[u][v]<<"\t"; } cout<<endl; } //利用公式將規格化量化係數矩陣轉換為逆量化後的係數矩陣 double IF[N][N];//逆量化後的係數矩陣 for(int i=0;i<N;i++){ for(int j=0;j<N;j++){//二維陣列Q 為亮度量化值表 IF[i][j] = 1.0*IF_[i][j]*brightnessQuantizedValueTable.Q[i][j]; } } /*輸出—逆量化後的係數矩陣*/ cout<<"逆量化後的係數矩陣:"<<endl; for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ cout<<IF[i][j]<<"\t"; } cout<<endl; } //由公式計算IDCT變化後的係數矩陣 double Iff[N][N]; for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ double sum = 0.0; for(int u=0;u<N;u++){ for(int v=0;v<N;v++){ sum = sum + (u==0?1.0/sqrt(2.0):1.0)*(v==0?1.0/sqrt(2.0):1.0)*IF[u][v]*cos((2*i+1)*u*PI*1.0/16)*cos((2*j+1)*v*PI*1.0/16); } } Iff[i][j] = 1.0/4*sum; } } /*輸出—IDCT變化後的係數矩陣*/ cout<<"IDCT變化後的係數矩陣:"<<endl; for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ //cout<<Iff[i][j]<<"\t"; printf("%.0f\t",Iff[i][j]); } cout<<endl; } cout<<"源影象的一個分量樣本的重構影象:"<<endl; /*IDCT變化後的係數矩陣+128後變成源影象的一個分量樣本的重構影象*/ double If[N][N]; for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ If[i][j] = Iff[i][j]+128; } } /*輸出—源影象的一個分量樣本的重構影象*/ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ //cout<<If[i][j]<<"\t"; printf("%.0f\t",If[i][j]); } cout<<endl; } return 0; }
測試用例(書本P83):