大數乘法的幾種算法分析及比較(2014騰訊南京筆試題)


來源:http://blog.csdn.net/chhuach2005/article/details/21168179

1.題目

       編寫兩個任意位數的大數相乘的程序,給出計算結果。

2.題目分析

       該題相繼被ACM、華為、騰訊等選作筆試、面試題,筆者2014年替師兄去騰訊筆試就遇到此題,當然若無准備要寫出這種程序,還是要花一定的時間的。故,覺得有必要深入研究一下。搜索了網上的大多數該類程序和算法,發現,大數乘法主要有模擬手工計算的普通大數乘法,分治算法和FFT算法。其中普通大數乘法占據了90%以上,其優點是空間復雜度低,實現簡單,時間復雜度為O(N²),分治算法雖然時間復雜度降低為,  

       但其實現需要配 合字符串模擬加減法操作,實現較為復雜,

    參考博客1http://cnn237111.blog.51cto.com/2359144/1201901 

    FFT算法則更為復雜,較少適用,有興趣

    參考博客2 http://blog.csdn.net/hondely/article/details/6938497

        和博客3http://blog.csdn.net/jackyguo1992/article/details/12613287

3.題目解答

3.1 逐位相乘處理進位法

        參考博客4的思路

        乘積是逐位相乘,也就是aibj,結果加入到積C的第i+j位,最后處理進位即可,例如:A =17 = 1*10 + 7 = (7,1)最后是十進制的冪表示法,冪次是從低位到高位,以下同。B=25 = 2*10 + 5 = (5, 2);(7 5, 7, 2) (35, 19, 2) (5, 22, 2) (5, 2. 4)=425。

原博客的思路為:
(1)轉換並反轉,字符串轉換為數字並將字序反轉;

(2)逐位相乘,結果存放在result_num[i+j]中;

(3)處理進位,消除多余的0;

(4)轉換並反轉,將計算結果轉換為字符串並反轉。

     原博客中采用指針參數傳遞,字符串長度有限制,改為通過string傳參數,按原思路編程如下:

頭文件和數據結構:

  1. #include <iostream>  
  2. #include <string>  
  3. #include <vector>  
  4. #include <stdlib.h>  
  5. using namespace std;  
  6. struct bigcheng  
  7. {  
  8.     vector<int> a;  
  9.     vector<int> b;  
  10.     string result_str;  
  11. };  
  12. void chartonum(string a,string b,bigcheng &tempcheng);//字符串轉換為數字並反轉  
  13. void multiply(bigcheng &tempchengh,vector<int> &result_num);//逐位相乘,處理進位消除多余的0  
  14. void numtochar(bigcheng &tempcheng,vector<int> &result_num);//將計算結果轉換為字符串並反轉  


(1)轉換並反轉,字符串轉換為數字並將字序反轉;

  1. void chartonum(string a,string b,bigcheng &tempcheng)  
  2. {  
  3.     int size_a=a.size();  
  4.     int size_b=b.size();  
  5.     for (int i=size_a-1;i>=0;i--)  
  6.     {  
  7.         tempcheng.a.push_back(a[i]-'0');  
  8.     }  
  9.     for (int i=size_b-1;i>=0;i--)  
  10.     {  
  11.         tempcheng.b.push_back(b[i]-'0');  
  12.     }  
  13. }  

(2)逐位相乘,結果存放在result_num[i+j]中;

(3)處理進位,消除多余的0;代碼為:

  1. void multiply(bigcheng &tempcheng,vector<int> &result_num)  
  2. {  
  3.     for (unsigned int i=0;i<tempcheng.a.size();i++)  
  4.     {  
  5.         for (unsigned int j=0;j<tempcheng.b.size();j++)  
  6.         {  
  7.             result_num[i+j]+=(tempcheng.a[i])*(tempcheng.b[j]);  
  8.         }  
  9.     }  
  10.     for (int i=result_num.size()-1;i>=0;i--)  
  11.     {  
  12.         if (result_num[i]!=0)  
  13.         {  
  14.             break;  
  15.         }  
  16.         else  
  17.             result_num.pop_back();  
  18.     }  
  19.     int c=0;  
  20.     for (unsigned int i=0;i<result_num.size();i++)//處理進位  
  21.     {  
  22.         result_num[i]+=c;  
  23.         c=result_num[i]/10;  
  24.         result_num[i]=result_num[i]%10;  
  25.     }  
  26.     if (c!=0)  
  27.     {  
  28.         result_num.push_back(c);  
  29.     }  
  30. }  

(4)轉換並反轉,將計算結果轉換為字符串並反轉。

  1. void numtochar(bigcheng &tempcheng,vector<int> &result_num)  
  2. {   int size=result_num.size();  
  3.     for (unsigned int i=0;i<result_num.size();i++)  
  4.     {  
  5.         tempcheng.result_str.push_back(char(result_num[size-1-i]+'0'));  
  6.     }  
  7. }  


主函數為:

  1. int main()  
  2. {  
  3.        bigcheng tempcheng;  
  4.     string a,b;  
  5.     cin>>a>>b;  
  6.     chartonum(a,b,tempcheng);  
  7.     vector<int> resultnum(a.size()+b.size(),0);  
  8.     multiply(tempcheng,resultnum);  
  9.     numtochar(tempcheng,resultnum);  
  10.     cout<<tempcheng.result_str<<endl;  
  11.     system("pause");  
  12.     return 0;  
  13. }  


     上面的思路還是很清晰的,但代碼有些過長,考慮優化如下:

(1)上述思路是先轉換反轉,其實無需先將全部字符串轉換為數字的,可即用即轉,節約空間;

(2)無需等到逐位相乘都結束,才處理進位,可即乘即進;

(3)無需等到所有結果出來后,將結果轉換為字符,可即乘即轉。

     優化后時間復雜度不變,但節省了空間,代碼更簡潔。如下:

頭文件和數據結構:

  1. #include <iostream>  
  2. #include <string>  
  3. #include <vector>  
  4. #include <stdlib.h>  
  5. #include <assert.h>  
  6. using namespace std;  
  7. struct bigcheng2  
  8. {  
  9.     string a;  
  10.     string b;  
  11.     string result_str;  
  12. };  
  13. void reverse_data( string &data);//字符串反轉  
  14. void multiply2(bigcheng2 &tempcheng2);//字符串模擬相乘  

字符串反轉:

  1. void reverse_data( string &data)  
  2. {  
  3.     char temp = '0';  
  4.     int start=0;  
  5.     int end=data.size()-1;  
  6.     assert( data.size()&& start <= end );  
  7.     while ( start < end )  
  8.     {  
  9.         temp = data[start];  
  10.         data[start++] = data[end];  
  11.         data[end--] = temp;  
  12.     }  
  13. }  


兩數相乘:

  1. void multiply2(bigcheng2 &tempcheng2)  
  2. {  
  3.     reverse_data(tempcheng2.a);//字符串反轉  
  4.     reverse_data(tempcheng2.b);  
  5.     int c=0;  
  6.     string temp(tempcheng2.a.size()+tempcheng2.b.size(),'0');//將temp全部初始化為0字符  
  7.     for (unsigned int i=0;i<tempcheng2.a.size();i++)  
  8.     {  
  9.         unsigned int j;  
  10.         for (j=0;j<tempcheng2.b.size();j++)  
  11.         {  
  12.             c+=temp[i+j]-'0'+(tempcheng2.a[i]-'0')*(tempcheng2.b[j]-'0');//注意temp[i+j]可能保存有上一次計算的結果  
  13.             temp[i+j]=(c%10)+'0';//將結果轉換為字符  
  14.             c=c/10;  
  15.         }  
  16.         while(c)  
  17.         {  
  18.             temp[i+j++]+=c%10;//temp里已存字符  
  19.             c=c/10;  
  20.         }  
  21.     }  
  22.     for (int i=temp.size()-1;i>=0;i--)  
  23.     {  
  24.         if (temp[i]!='0')  
  25.             break;  
  26.         else  
  27.             temp.pop_back();  
  28.     }  
  29.     reverse_data(temp);//結果?字Á?符¤?串ä?反¤¡ä轉Áa  
  30.     tempcheng2.result_str=temp;  
  31. }  

主函數:

  1. int main()  
  2. {  
  3.        bigcheng2 tempcheng2;  
  4.        string a,b;  
  5.        cin>>a>>b;  
  6.        tempcheng2.a=a;  
  7.        tempcheng2.b=b;  
  8.        multiply2(tempcheng2);  
  9.        cout<<tempcheng2.result_str<<endl;  
  10.        system("pause");  
  11.        return 0;  
  12. }  

3.2 移位進位法

       移位進位法也是普通的大數相乘算法,其時間復雜度也為O(N²)其基本思路參考博客5,簡述如下:

 

  按照乘法的計算過程來模擬計算:

       1 2

    × 3 6

   ---------- ---- 其中,上標數字為進位數值。

     71 2  --- 在這個計算過程中,2×6=12。本位保留2,進位為1.這里是一個簡單的計算過程,如果在高位也需要進位的情況下,如何處理?

    3 6

    -----------

    413  2

        其代碼優化如下:

  1. #include <iostream>  
  2. #include <string>  
  3. #include <vector>  
  4. #include <stdlib.h>  
  5. #include <assert.h>  
  6. using namespace std;  
  7. void reverse_data( string &data);//字符串反轉  
  8. void compute_value( string lhs,string rhs,string &result );  
  9. void reverse_data( string &data)  
  10. {  
  11.     char temp = '0';  
  12.     int start=0;  
  13.     int end=data.size()-1;  
  14.     assert( data.size()&& start <= end );  
  15.     while ( start < end )  
  16.     {  
  17.         temp = data[start];  
  18.         data[start++] = data[end];  
  19.         data[end--] = temp;  
  20.     }  
  21. }  
  22. void compute_value( string lhs,string rhs,string &result )  
  23. {  
  24.     reverse_data(lhs);  
  25.     reverse_data(rhs);  
  26.     int i = 0, j = 0, res_i = 0;  
  27.     int tmp_i = 0;  
  28.     int carry = 0;  
  29.   
  30.     for ( i = 0; i!=lhs.size(); ++i, ++tmp_i )  
  31.     {  
  32.         res_i = tmp_i;  //在每次計算時,結果存儲的位需要增加  
  33.         for ( j = 0; j!= rhs.size(); ++j )  
  34.         {  
  35.             carry += ( result[res_i] - '0' )+(lhs[i] - '0') * (rhs[j] - '0');//此處注意,每次計算並不能保證以前計算結果的進位都消除, 並且以前的計算結果也需考慮。  
  36.             result[res_i++] = ( carry % 10 + '0' );  
  37.             carry /= 10;  
  38.         }  
  39.         while (carry)//乘數的一次計算完成,可能存在有的進位沒有處理  
  40.         {  
  41.             result[res_i++] = (carry % 10 + '0');  
  42.             carry /= 10;  
  43.         }  
  44.     }  
  45.     for (int i=result.size()-1;i>=0;i--)  
  46.     {  
  47.         if (result[i]!='0')  
  48.             break;  
  49.         else  
  50.             result.pop_back();  
  51.     }  
  52.         reverse_data(result);  
  53. }  
  54. int main()  
  55. {  
  56.     string a,b;  
  57.     cin>>a>>b;  
  58.     string result(a.size()+b.size(),'0');  
  59.     compute_value(a,b,result);  
  60.     cout<<result<<endl;  
  61.     system("pause");  
  62.     return 0;  
  63. }  

 

3.3運行結果

        運行結果如圖1、圖2所示

   

                    圖1    

 

                                                                    圖2


免責聲明!

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



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