和整數相乘_[演算法]大整數乘法
阿新 • • 發佈:2021-01-24
技術標籤:和整數相乘
利用二分法和遞迴計算任意長度整數相乘
以下複雜度分析有問題,在於 劃分為 A12(n2),這樣才相當於移位;
程式中採用string直接+'0'的方式來*10
第一次的程式碼有漏洞,已更正
我們可以把規模n變成n/2和n/2(把以1位為單位規模為n的問題 變成 以n/2為單位的規模為2的問題),把規模m變成m/2和m/2(把以1位為單位規模為m的問題 變成 以m/2為單位的規模為2的問題),如此,原來的大整數相乘就變成了兩個2位數相乘,只不過低位向高位的進位制不再是10,而是和。更一般地,我們把整數A由規模n分為n1和n2,把整數B由規模m分為m1和m2,如下圖:
則A分為n1位的A1和n2位,B分為m1位的B1和m2位的B2,如下式所示:
以此類推,我們可以把A1、A2、B1、B2繼續劃分,直至最小單位。(這裡在程式設計時需要用遞迴實現)
於是
注意用2^n做乘法相當於移位運算,需要O(n)時間。此公式中有4次乘法運算,3次加法運算,則
故T(n)=O(n^2)
而
A1B1 A2B2已經計算過,故乘法運算次數減少為3次
產生遞推式
// 四次乘法,3次加法 #include <iostream> #include <string> #include <sstream> using namespace std; string multi(string A, string B); //計算大整數相乘 string Plus(string q, string w, string e, string r); //計算大整數相加 stringstream ss; int main() { string A, B; while (cin >> A >> B) { cout << multi(A, B) << endl; } return 0; } string multi(string A, string B) { int len_A = A.length(); int len_B = B.length(); if (len_A == 1) { if (len_B == 1) { //最基本的情況:A和B都是一位數,把A、B從string轉為int(我這裡用的stringstream),然後相乘後轉回為string型return回去。 ss << A; int a; ss >> a; ss.clear(); ss << B; int b; ss >> b; ss.clear(); ss << b*a; string str_out; ss >> str_out; ss.clear(); return str_out; } else {//A是個位數,B不是的情況下,按照分治的思想把B分開分別與A相乘。 string B1, B2; B1 = B.substr(0, len_B / 2); B2 = B.substr(len_B / 2); string AB1 = multi(A, B1); string AB2 = multi(A, B2); cout<<AB1<<endl<<AB2<<endl; if (AB2.length() > B2.length()) { //此時AB2最多比B2多出一位 string str = AB2.substr(0, 1); /*ss << AB1; 漏洞,此時AB1可能已經超出int範圍了 int ab1; ss >> ab1;*/ string tp0(AB1.length()-1,'0'); str=tp0+str; string u0(AB1.length(),'0'); AB1=Plus(AB1,str,u0,u0); return AB1 + AB2.substr(1); } else return AB1 + AB2; } } else { if (len_B == 1) {//B是個位數,A不是的情況與上述A是個位數B不是的情況相同。 string A1, A2; A1 = A.substr(0, len_A / 2); A2 = A.substr(len_A / 2); string A1B = multi(A1, B); string A2B = multi(A2, B); if (A2B.length() > A2.length()) { string str = A2B.substr(0, 1); string tp0(A1B.length()-1,'0'); str=tp0+str; string u0(A1B.length(),'0'); A1B=Plus(A1B,str,u0,u0); return A1B + A2B.substr(1); } else { return A1B + A2B; } } else {//A和B都不是個位數,就按照上述方法分治就可以了,只是為了最後相加的時候方便,把返回的四個部分都用0湊成了位數相同的。 string A1, A2, B1, B2; A1 = A.substr(0, len_A / 2); A2 = A.substr(len_A / 2); B1 = B.substr(0, len_B / 2); B2 = B.substr(len_B / 2); string part1_ = multi(A1, B1); string part1_0(A2.length()+B2.length(), '0'); //長度為A2.length()+B2.length()的0串 part1_ = part1_ + part1_0; //A1B1*10(n2,m2) string part2_ = multi(A2, B2); string part2_00(part1_.length() - part2_.length(), '0'); part2_ = part2_00 + part2_; //A2B2,高位補0 string part3_ = multi(A1, B2); string part3_0(A2.length(), '0'); part3_ = part3_ + part3_0; //A1B2*10(n2) string part3_00(part1_.length() - part3_.length(), '0'); part3_ = part3_00 + part3_; string part4_ = multi(A2, B1); string part4_0(B2.length(), '0'); part4_ = part4_ + part4_0; string part4_00(part1_.length() - part4_.length(), '0'); part4_ = part4_00 + part4_; return Plus(part1_, part2_, part3_, part4_); //未改進的第一種演算法 } } } string Plus(string q, string w, string e, string r) { //大整數相加,qwer位數相同 int len_q = q.length(); string y, out; int a, b, c, d; for (int i = 1; i <= len_q; i++) { //逐位相加 ss << q.substr(len_q - i, 1); ss >> a; ss.clear(); ss << w.substr(len_q - i, 1); ss >> b; ss.clear(); ss << e.substr(len_q - i, 1); ss >> c; ss.clear(); ss << r.substr(len_q - i, 1); ss >> d; ss.clear(); ss << a + b + c + d; ss >> y; ss.clear(); if (i == 1) out = y; else if (out.length() > i - 1) { //進一位 ss << out.substr(0, 1); ss >> a; ss.clear(); ss << y; ss >> b; ss.clear(); ss << a + b; ss >> y; ss.clear(); out = y + out.substr(1); } else { out = y + out; } } return out; }
// 三次乘法。由於還沒有實現大整數減法(或支援負數的加法),暫時還是虛擬碼 #include <iostream> #include <string> #include <sstream> using namespace std; string multi(string A, string B); //計算大整數相乘 string Plus(string q, string w, string e, string r); //計算大整數相加 stringstream ss; int main() { string A, B; while (cin >> A >> B) { cout << multi(A, B) << endl; } return 0; } string multi(string A, string B) { int len_A = A.length(); int len_B = B.length(); if (len_A == 1) { if (len_B == 1) { //最基本的情況:A和B都是一位數,把A、B從string轉為int(我這裡用的stringstream),然後相乘後轉回為string型return回去。 ss << A; int a; ss >> a; ss.clear(); ss << B; int b; ss >> b; ss.clear(); ss << b*a; string str_out; ss >> str_out; ss.clear(); return str_out; } else {//A是個位數,B不是的情況下,按照分治的思想把B分開分別與A相乘。 string B1, B2; B1 = B.substr(0, len_B / 2); B2 = B.substr(len_B / 2); string AB1 = multi(A, B1); string AB2 = multi(A, B2); cout<<AB1<<endl<<AB2<<endl; if (AB2.length() > B2.length()) { //此時AB2最多比B2多出一位 string str = AB2.substr(0, 1); /*ss << AB1; 漏洞,此時AB1可能已經超出int範圍了 int ab1; ss >> ab1;*/ string tp0(AB1.length()-1,'0'); str=tp0+str; string u0(AB1.length(),'0'); AB1=Plus(AB1,str,u0,u0); return AB1 + AB2.substr(1); } else return AB1 + AB2; } } else { if (len_B == 1) {//B是個位數,A不是的情況與上述A是個位數B不是的情況相同。 string A1, A2; A1 = A.substr(0, len_A / 2); A2 = A.substr(len_A / 2); string A1B = multi(A1, B); string A2B = multi(A2, B); if (A2B.length() > A2.length()) { string str = A2B.substr(0, 1); string tp0(A1B.length()-1,'0'); str=tp0+str; string u0(A1B.length(),'0'); A1B=Plus(A1B,str,u0,u0); return A1B + A2B.substr(1); } else { return A1B + A2B; } } else {//A和B都不是個位數,就按照上述方法分治就可以了,只是為了最後相加的時候方便,把返回的四個部分都用0湊成了位數相同的。 string A1, A2, B1, B2; A1 = A.substr(0, len_A / 2); A2 = A.substr(len_A / 2); B1 = B.substr(0, len_B / 2); B2 = B.substr(len_B / 2); string part1_ = multi(A1, B1); part1_ = multi(2, part1_); string part1_0(A2.length()+B2.length(), '0'); //長度為A2.length()+B2.length()的0串 part1_ = part1_ + part1_0; //2*A1B1*10(n2,m2) string part2_ = multi(A2, B2); part2_ =multi(2,part2_); string part2_00(part1_.length() - part2_.length(), '0'); part2_ = part2_00 + part2_; //2*A2B2,高位補0 string part3_ = A1; string part3_0(A2.length(), '0'); //(A1*10(n2)-A2) part3_ = part3_ + part3_0; //A1*10(n2) 虛擬碼 part3_= part_3-A2 //大整數減法(或支援負數的大整數加法),未實現 string part4_ = B1; //(B2-B1*10(m2)) string part4_0=(B2.length(), '0'); part4_ = part4_ + part4_0; part4_= B2-part4_; part5_= multi(part4_,part3_) string part5_00(part1_.length() - part5_.length(), '0'); part5_ = part5_00 + part5_; string u0(part1_.length(), '0'); return Plus(part1_, part2_, part5_, u0); } } } string Plus(string q, string w, string e, string r) { //大整數相加,qwer位數相同 int len_q = q.length(); string y, out; int a, b, c, d; for (int i = 1; i <= len_q; i++) { //逐位相加 ss << q.substr(len_q - i, 1); ss >> a; ss.clear(); ss << w.substr(len_q - i, 1); ss >> b; ss.clear(); ss << e.substr(len_q - i, 1); ss >> c; ss.clear(); ss << r.substr(len_q - i, 1); ss >> d; ss.clear(); ss << a + b + c + d; ss >> y; ss.clear(); if (i == 1) out = y; else if (out.length() > i - 1) { //進一位 ss << out.substr(0, 1); ss >> a; ss.clear(); ss << y; ss >> b; ss.clear(); ss << a + b; ss >> y; ss.clear(); out = y + out.substr(1); } else { out = y + out; } } return out; }
參考
https://blog.csdn.net/qq_36165148/article/details/81132525blog.csdn.net