分治法——大数相乘 大数相乘(分治法)

大数相乘(分治法)

前言:

A,B两个大数,都为n位,要计算A*B,需要将A和B划分成两等份,如下图所示

普通的做法是A*B=a1*b1*10^n+(a1*b0+b1*a0)*10^(2/n)+a0*b0

举个例子:

1234*9876=(12*98)*10000+(12*76+98*34)*100+34*76

对于这个算法的时间复杂度,我们需要做4次n/2级别的乘法和3加法。即T(n)=4*T(n/2)+O(n),时间复杂度是O(n²).

分治法的算法是A*B=a1*b1*10^n+[(a1+a0)*(b0+b1)-a1*a0-b1*b0]*10^n/2+a0*b0

对于这个算法的时间复杂度,我们需要做3次n/2级别的乘法。即T(n)=3*T(n/2)+O(n),时间复杂度是T(n) = O(n^log2(3) ) = O(n^1.59 ).

下面上代码:


     
     
  1. #include<iostream>
  2. #include<string>
  3. #include<algorithm>
  4. #include<sstream>
  5. using namespace std;
  6. //string转换为int
  7. int strint(string s) {
  8. int num;
  9. stringstream ss(s);
  10. ss >> num;
  11. return num;
  12. }
  13. //int转换为string
  14. string intstr(int num) {
  15. string s;
  16. stringstream ss;
  17. ss << num;
  18. ss >> s;
  19. return s;
  20. }
  21. //前置0 ,因为大数的长度必须要为2^n的倍数,才能划分
  22. void removePrezero(string &s) {
  23. if (s.length() == 1) return;
  24. int k = 0;
  25. for ( int i = 0; i < s.length(); i++) {
  26. if (s[i] == '0')k++;
  27. else break;
  28. }
  29. if (k == s.length())s = "0";
  30. else s = s.substr(k);
  31. }
  32. //大数相加
  33. string add(string s1, string s2) {
  34. string s = "";
  35. removePrezero(s1); //去掉前置0
  36. removePrezero(s2);
  37. reverse(s1.begin(), s1.end()); //先把字符串颠倒,方便相加
  38. reverse(s2.begin(), s2.end());
  39. int c = 0;
  40. for ( int i = 0;c|| i < max(s1.length(), s2.length()); i++) {
  41. int t = c;
  42. if (i < s1.length())t += s1[i] - '0';
  43. if (i < s2.length())t += s2[i] - '0';
  44. int d = t % 10;
  45. s.insert( 0, intstr(d));
  46. c = t / 10;
  47. }
  48. return s;
  49. }
  50. //大数相减
  51. string subtra(string &s1, string &s2) {
  52. string s = "";
  53. string flag;
  54. removePrezero(s1); //去掉前置0
  55. removePrezero(s2);
  56. int len1 = s1.length();
  57. int len2 = s2.length();
  58. int len = len1>len2 ? len1 : len2;
  59. if (len1 < len2)flag = "-";
  60. else if (len1 > len2)flag = "+";
  61. else {
  62. int i;
  63. for (i = 0; i < len1; i++) {
  64. if (s1.at(i) > s2.at(i)) {
  65. flag = "+"; break;
  66. }
  67. else if (s1.at(i) < s2.at(i)) {
  68. flag = "-"; break;
  69. }
  70. }
  71. if (i == len1)s == "0";
  72. }
  73. int* num = ( int*) malloc( sizeof( int)*len);
  74. reverse(s1.begin(), s1.end()); //方便相减
  75. reverse(s2.begin(), s2.end());
  76. int c = 0;
  77. for ( int j = 0; j < len; j++) {
  78. int n1 = j < len1 ? s1[j] - '0' : 0;
  79. int n2 = j < len2 ? s2[j] - '0' : 0;
  80. if (flag == "+")num[c++] = n1 - n2;
  81. else num[c++] = n2 - n1;
  82. }
  83. for ( int j = 0; j < c; j++) {
  84. if (num[j] < 0) {
  85. num[j] += 10; num[j + 1] -= 1;
  86. }
  87. }
  88. c--;
  89. while (num[c] == 0)c--;
  90. for ( int j = 0; j <=c; j++){
  91. s.insert( 0, intstr(num[j]));
  92. }
  93. if (flag == "-") return flag + s;
  94. else return s;
  95. }
  96. //增加前置0
  97. void addPrezero(string &s, int L) {
  98. for ( int i = 0; i < L; i++)
  99. s = s.insert( 0, "0");
  100. }
  101. //增加后置0,大数*10^n 相当于在数的后面加n个0
  102. string addLastzero(string s, int L) {
  103. string s1 = s;
  104. for ( int i = 0; i < L; i++)
  105. s1 += "0";
  106. return s1;
  107. }
  108. //大数相乘
  109. string multi(string &s1, string &s2) {
  110. bool flag1 = false, flag2 = false;
  111. string sign; //作用是判断结果是正数还是负数
  112. if (s1.at( 0) == '-') {
  113. flag1 = true; s1 = s1.substr( 1);
  114. }
  115. if (s1.at( 0) == '-') {
  116. flag2 = true; s2 = s2.substr( 1);
  117. }
  118. if (flag1&&flag2)sign = "+";
  119. else if (flag1 || flag2) sign = "-";
  120. else sign = "+";
  121. int L = 4; //将数前置若干个0,使其长度为2^n的倍数
  122. if (s1.length() > 2 || s2.length() > 2) {
  123. if (s1.length() >= s2.length()) {
  124. while (L < s1.length())L *= 2;
  125. if (L != s1.length())
  126. addPrezero(s1, L - s1.length());
  127. addPrezero(s2, L - s2.length());
  128. } else {
  129. while (L < s2.length())L*= 2;
  130. if (L != s2.length())
  131. addPrezero(s2, L - s2.length());
  132. addPrezero(s1, L - s1.length());
  133. }
  134. }
  135. if (s1.length() == 1)addPrezero(s1, 1);
  136. if (s2.length() == 1)addPrezero(s2, 1);
  137. int n = s1.length();
  138. string result, a0, a1, b0, b1;
  139. if (n > 1) {
  140. a1 = s1.substr( 0, n / 2);
  141. a0 = s1.substr(n / 2, n);
  142. b1 = s2.substr( 0, n / 2);
  143. b0 = s2.substr(n / 2, n);
  144. }
  145. if (n == 2) {
  146. int x1 = strint(a1);
  147. int x2 = strint(a0);
  148. int y1 = strint(b1);
  149. int y2 = strint(b0);
  150. int num = (x1 * 10 + x2)*(y1 * 10 + y2);
  151. result = intstr(num);
  152. } else {
  153. string c2 = multi(a1, b1);
  154. string c0 = multi(a0, b0);
  155. string temp1 = add(a0, a1);
  156. string temp2 = add(b1, b0);
  157. string temp3 = add(c2, c0);
  158. string temp_c1 = multi(temp1, temp2);
  159. string c1 = subtra(temp_c1, temp3);
  160. string s1 = addLastzero(c1, n / 2);
  161. string s2 =addLastzero(c2, n);
  162. result = add(add(s1, s2), c0);
  163. }
  164. if (sign == "-")result.insert( 0, sign);
  165. return result;
  166. }
  167. int main() {
  168. //text
  169. string s1 = "-1234999999999999999999999", s2 = "12348976543";
  170. cout << multi(s1, s2);
  171. }

总结:算法本身不难,主要是程序有太多需要注意的细节。对代码有疑问或者有些部分不理解的朋友,欢迎留言。

对了,程序的主要思想参考于点击打开链接,强烈建议大家可以去看一下。

发布了25 篇原创文章 · 获赞 5 · 访问量 329

前言:

A,B两个大数,都为n位,要计算A*B,需要将A和B划分成两等份,如下图所示

普通的做法是A*B=a1*b1*10^n+(a1*b0+b1*a0)*10^(2/n)+a0*b0

举个例子:

1234*9876=(12*98)*10000+(12*76+98*34)*100+34*76

对于这个算法的时间复杂度,我们需要做4次n/2级别的乘法和3加法。即T(n)=4*T(n/2)+O(n),时间复杂度是O(n²).

分治法的算法是A*B=a1*b1*10^n+[(a1+a0)*(b0+b1)-a1*a0-b1*b0]*10^n/2+a0*b0

对于这个算法的时间复杂度,我们需要做3次n/2级别的乘法。即T(n)=3*T(n/2)+O(n),时间复杂度是T(n) = O(n^log2(3) ) = O(n^1.59 ).

下面上代码:


  
  
  1. #include<iostream>
  2. #include<string>
  3. #include<algorithm>
  4. #include<sstream>
  5. using namespace std;
  6. //string转换为int
  7. int strint(string s) {
  8. int num;
  9. stringstream ss(s);
  10. ss >> num;
  11. return num;
  12. }
  13. //int转换为string
  14. string intstr(int num) {
  15. string s;
  16. stringstream ss;
  17. ss << num;
  18. ss >> s;
  19. return s;
  20. }
  21. //前置0 ,因为大数的长度必须要为2^n的倍数,才能划分
  22. void removePrezero(string &s) {
  23. if (s.length() == 1) return;
  24. int k = 0;
  25. for ( int i = 0; i < s.length(); i++) {
  26. if (s[i] == '0')k++;
  27. else break;
  28. }
  29. if (k == s.length())s = "0";
  30. else s = s.substr(k);
  31. }
  32. //大数相加
  33. string add(string s1, string s2) {
  34. string s = "";
  35. removePrezero(s1); //去掉前置0
  36. removePrezero(s2);
  37. reverse(s1.begin(), s1.end()); //先把字符串颠倒,方便相加
  38. reverse(s2.begin(), s2.end());
  39. int c = 0;
  40. for ( int i = 0;c|| i < max(s1.length(), s2.length()); i++) {
  41. int t = c;
  42. if (i < s1.length())t += s1[i] - '0';
  43. if (i < s2.length())t += s2[i] - '0';
  44. int d = t % 10;
  45. s.insert( 0, intstr(d));
  46. c = t / 10;
  47. }
  48. return s;
  49. }
  50. //大数相减
  51. string subtra(string &s1, string &s2) {
  52. string s = "";
  53. string flag;
  54. removePrezero(s1); //去掉前置0
  55. removePrezero(s2);
  56. int len1 = s1.length();
  57. int len2 = s2.length();
  58. int len = len1>len2 ? len1 : len2;
  59. if (len1 < len2)flag = "-";
  60. else if (len1 > len2)flag = "+";
  61. else {
  62. int i;
  63. for (i = 0; i < len1; i++) {
  64. if (s1.at(i) > s2.at(i)) {
  65. flag = "+"; break;
  66. }
  67. else if (s1.at(i) < s2.at(i)) {
  68. flag = "-"; break;
  69. }
  70. }
  71. if (i == len1)s == "0";
  72. }
  73. int* num = ( int*) malloc( sizeof( int)*len);
  74. reverse(s1.begin(), s1.end()); //方便相减
  75. reverse(s2.begin(), s2.end());
  76. int c = 0;
  77. for ( int j = 0; j < len; j++) {
  78. int n1 = j < len1 ? s1[j] - '0' : 0;
  79. int n2 = j < len2 ? s2[j] - '0' : 0;
  80. if (flag == "+")num[c++] = n1 - n2;
  81. else num[c++] = n2 - n1;
  82. }
  83. for ( int j = 0; j < c; j++) {
  84. if (num[j] < 0) {
  85. num[j] += 10; num[j + 1] -= 1;
  86. }
  87. }
  88. c--;
  89. while (num[c] == 0)c--;
  90. for ( int j = 0; j <=c; j++){
  91. s.insert( 0, intstr(num[j]));
  92. }
  93. if (flag == "-") return flag + s;
  94. else return s;
  95. }
  96. //增加前置0
  97. void addPrezero(string &s, int L) {
  98. for ( int i = 0; i < L; i++)
  99. s = s.insert( 0, "0");
  100. }
  101. //增加后置0,大数*10^n 相当于在数的后面加n个0
  102. string addLastzero(string s, int L) {
  103. string s1 = s;
  104. for ( int i = 0; i < L; i++)
  105. s1 += "0";
  106. return s1;
  107. }
  108. //大数相乘
  109. string multi(string &s1, string &s2) {
  110. bool flag1 = false, flag2 = false;
  111. string sign; //作用是判断结果是正数还是负数
  112. if (s1.at( 0) == '-') {
  113. flag1 = true; s1 = s1.substr( 1);
  114. }
  115. if (s1.at( 0) == '-') {
  116. flag2 = true; s2 = s2.substr( 1);
  117. }
  118. if (flag1&&flag2)sign = "+";
  119. else if (flag1 || flag2) sign = "-";
  120. else sign = "+";
  121. int L = 4; //将数前置若干个0,使其长度为2^n的倍数
  122. if (s1.length() > 2 || s2.length() > 2) {
  123. if (s1.length() >= s2.length()) {
  124. while (L < s1.length())L *= 2;
  125. if (L != s1.length())
  126. addPrezero(s1, L - s1.length());
  127. addPrezero(s2, L - s2.length());
  128. } else {
  129. while (L < s2.length())L*= 2;
  130. if (L != s2.length())
  131. addPrezero(s2, L - s2.length());
  132. addPrezero(s1, L - s1.length());
  133. }
  134. }
  135. if (s1.length() == 1)addPrezero(s1, 1);
  136. if (s2.length() == 1)addPrezero(s2, 1);
  137. int n = s1.length();
  138. string result, a0, a1, b0, b1;
  139. if (n > 1) {
  140. a1 = s1.substr( 0, n / 2);
  141. a0 = s1.substr(n / 2, n);
  142. b1 = s2.substr( 0, n / 2);
  143. b0 = s2.substr(n / 2, n);
  144. }
  145. if (n == 2) {
  146. int x1 = strint(a1);
  147. int x2 = strint(a0);
  148. int y1 = strint(b1);
  149. int y2 = strint(b0);
  150. int num = (x1 * 10 + x2)*(y1 * 10 + y2);
  151. result = intstr(num);
  152. } else {
  153. string c2 = multi(a1, b1);
  154. string c0 = multi(a0, b0);
  155. string temp1 = add(a0, a1);
  156. string temp2 = add(b1, b0);
  157. string temp3 = add(c2, c0);
  158. string temp_c1 = multi(temp1, temp2);
  159. string c1 = subtra(temp_c1, temp3);
  160. string s1 = addLastzero(c1, n / 2);
  161. string s2 =addLastzero(c2, n);
  162. result = add(add(s1, s2), c0);
  163. }
  164. if (sign == "-")result.insert( 0, sign);
  165. return result;
  166. }
  167. int main() {
  168. //text
  169. string s1 = "-1234999999999999999999999", s2 = "12348976543";
  170. cout << multi(s1, s2);
  171. }

总结:算法本身不难,主要是程序有太多需要注意的细节。对代码有疑问或者有些部分不理解的朋友,欢迎留言。

对了,程序的主要思想参考于点击打开链接,强烈建议大家可以去看一下。

猜你喜欢

转载自blog.csdn.net/weixin_44602007/article/details/105083089