算法导论课作业:分治法求解大整数乘法 – 学号:20204227058

求解思想

​ 实现大整数乘法的方法有许多种,其中我们最简单的方法就是小学里面教的竖式算法,这种方法在计算过程中数AAA需要和数BBB中的每一位数相乘,并且最终对每一位进行相加,就整体而言,复杂度为O(n2)O(n^2)O(n2),对每一位需要进行相乘和进位即为O(n),又要对n位进行操作,最终结果就是O(n2)O(n^2)O(n2)。
​ 为此,我们采用分治法来处理大数乘法,进而进行算法复杂度降低的尝试,我们首先将两个数字AAA和BBB各自分成前后两段,当然这种时候可能存在两边数字长度不一样,而且比较长的一边不是偶数导致无法整除的方法,所以我们采用前方填充0的办法把两个数字填充成长度大于最长部分的前方填充0的偶长字符串。
​ 接下来,我们将AAA分成A1,A2A_1, A_2A1​,A2​, 同时将BBB分成B1B_1B1​和B2B_2B2​, 然后原本的计算公式就变成了 A1∗B1∗10N+(A1∗B2+A2∗B1)∗10N/2+A2∗B2A_1 * B_1 * 10^N + (A_1 * B_2 + A_2 * B_1) * 10^{N/2} +A_2 * B_2A1​∗B1​∗10N+(A1​∗B2​+A2​∗B1​)∗10N/2+A2​∗B2​,这样一来,递归每一步的时间复杂度就变成了O(n)O(n)O(n),主要是加法,分治则包括四个子问题,每个子问题的规模是原本的1/21/21/2, 也就是T(n)=4T(n/2)+O(n)T(n)=4T(n/2)+O(n)T(n)=4T(n/2)+O(n), 一层层递归就是n+4∗n/2+...+4log2n∗1n+4*n/2+...+4^{log_2^{n}}*1n+4∗n/2+...+4log2n​∗1, 然后后面我们用一个换底公式把最后一项改一下,结果就是nlog24n^{log_2^4}nlog24​,也就是n2n^2n2, 我们这里可以直接用最大项,当然其实就不那么看,我们也可以大致得出,这个复杂度如果算上一些小的项目可能已经比原本的竖式算法还要大了,为此,我们必须要想办法去把复杂度降下来。
​ 降低复杂度的方法就是我们再去改一下我们的公式,我们把计算公式中间那一部分给替换成如下公式,也就是
(A1∗B2+A2∗B1)=(A1−A2)∗(B2−B1)+A1∗B1+A2∗B2(A_1*B_2 + A_2*B_1) = (A_1 - A_2) * (B_2 - B_1) + A_1 * B_1 + A_2 * B_2 (A1​∗B2​+A2​∗B1​)=(A1​−A2​)∗(B2​−B1​)+A1​∗B1​+A2​∗B2​
​ 这时候,我们发现之前已经计算过的A1∗B1A_1 * B_1A1​∗B1​ 和 A2∗B2A_2 * B_2A2​∗B2​都被复用了,也就是说,我们乘法只需要计算三次了。这时候,我们来分析下这时候的复杂度,同样也是上面的方法,我们最后可以看成T(n)=3T(n/2)+O(n)T(n)=3T(n/2)+O(n)T(n)=3T(n/2)+O(n), 最后计算的时候就是n+3∗n/2+...+3log2n∗1n+3*n/2+...+3^{log_2^n}*1n+3∗n/2+...+3log2n​∗1,最后一项也就变成了nlog23n^{log_2^3}nlog23​, 最后一项的大小开始比n2n^2n2要小了,这时候我们需要对这个等比数列来进行求和,结果如下
n∗(3/2)log2n−13/2−1=n∗2∗(nlog23nlog22−1)=2∗n∗(nlog23−log22−1)=2n(nlog23/2−1)=2nlog23.5−2n<n2n*\frac{(3/2)^{log_2^{n}}-1}{3/2-1}=n*2*(\frac{n^{log_2^3}}{n^{log_2^2}}-1)= 2 * n*(n^{log_2^3-log_2^2}-1)=2n(n^{log_2^{3/2}}-1)=2n^{log_2^{3.5}}-2n < n^2 n∗3/2−1(3/2)log2n​−1​=n∗2∗(nlog22​nlog23​​−1)=2∗n∗(nlog23​−log22​−1)=2n(nlog23/2​−1)=2nlog23.5​−2n<n2

​ 所以,我们可以看出来,分治之后的复杂度是要比O(n2)O(n^2)O(n2)要小的,就是O(nlog23.5)O(n^{log_2^{3.5}})O(nlog23.5​),这时候我们就能基本证明复杂度的降低了,就算是我们单步骤中增加了减法,我们的系数变大,但在nnn变得足够大的时候也是会比O(n2)O(n^2)O(n2)小的。
​ 但是我们从算法中可以看到,计算过程中可能出现负数,当然这也是最麻烦的,我们在计算的过程中,需要考虑到负数,加法减法,为此,我们需要做一些分类讨论,首先就是要对同号相乘和异号相乘进行分类,然后在计算减法的时候也要进行分类讨论,比较两者的绝对值,被减数的绝对值比减数大的情况和比减数小的都要进行分类,计算过程中,我们同样都是采取从后往前计算的方式,采用借位的方式。

实验结果
本地运行
样例输入

123146123642387468 2349798237482752389792743278947239374
9282938509830928 384508023485409328329458
3459082390485092348580 329485032405802945
800 6003
983 123
-345898237957 92743958752

样例输出

289368544287715176259369109686569450197981809053765032
3569364338551681136516034509668261877024
1139715873523322969051326908210730568100
4802400
120909
-32079971913473488749664

运行结果(图片)

代码
#include<string>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<sstream>
using namespace std;class Solution {public:/*这个算法主要分成几个模块来进行实现1. 在字符串屁股后面添加N个02. 字符串相加(这里包括了对负数的操作)【本算法最复杂部分,因为负数操作的加入可能使得最终费用相对于原本还变高】3. 把字符串变成偶数串5. 终止条件:N=1/0的时候6. 字符串转数字相乘然后再转回字符串7. 把前面的0给去掉*/string multiply(string num1, string num2) {string result = multi(num1, num2);result = cut_zero(result);return result;}string multi(string num1, string num2){//cout<< "pre:" << num1 <<" " << num2 << endl;int final_flag = 1, flag_1 = 1, flag_2 = 1;if(num1[0] == '-'){flag_1 = -1;num1 = num1.substr(1);}if(num2[0] == '-'){flag_2 = -1;num2 = num2.substr(1);}// 如果异号,我们采用if(flag_1 + flag_2 == 0){final_flag = -1;}int maxsize = num1.size() > num2.size() ? num1.size() : num2.size();if(maxsize % 2 == 1){maxsize += 1;}num1 = to_odd_string(num1, maxsize);num2 = to_odd_string(num2, maxsize);//cout<< "dealed:" << num1 <<" " << num2 << endl;if(judge_zero(num1) || judge_zero(num2)){return "0";}if(maxsize <= 4){string res =  convert_multi(num1, num2);if(final_flag == -1){string ret = "-";res = ret + res;}//cout << "result:" << res << endl;return res;}int N_2 = num1.size() / 2;string A_1 = num1.substr(0, N_2);string A_2 = num1.substr(N_2, N_2);string B_1 = num2.substr(0, N_2);string B_2 = num2.substr(N_2, N_2);string _A_2 = "-";_A_2 = _A_2 + A_2;string _B_1 = "-";_B_1 = _B_1 + B_1;string A_1dotB_1 = multi(A_1, B_1);string A_2dotB_2 = multi(A_2, B_2);string A_1_A_2 = add(A_1, _A_2);string B_2_B_1 = add(B_2, _B_1);string A_1_A_2dotB_2_B_1 = multi(A_1_A_2, B_2_B_1);string first = A_1dotB_1 + generate_zeros(N_2 * 2);string second = A_1_A_2dotB_2_B_1 + generate_zeros(N_2);string third = A_2dotB_2;string forth = A_1dotB_1 + generate_zeros(N_2);string fifth = A_2dotB_2 + generate_zeros(N_2);string result = add(first, second);result = add(result, third);result = add(result, forth);result = add(result, fifth);if(final_flag == -1){string ret = "-";result = ret + result;}//cout << "result:" << result << endl;return result;}string convert_multi(string num1, string num2){int n_1 = str2num(num1);int n_2 = str2num(num2);return num2str(n_1 * n_2);}string add(string num1, string num2){int flag_1 = 1, flag_2 = 1;if(num1[0] == '-'){flag_1 = -1;}if(num2[0] == '-'){flag_2 = -1;}if(flag_1 + flag_2 == 0){// 两个数字一正一负if(flag_1 == -1){num1 = num1.substr(1);if(compare(num1, num2)){// 负数绝对值比正数大 num1绝对值-num2绝对值,返回值符号为负string fret = "-";return fret + single_minus(num1, num2);}else{// 负数绝对值比正数小 num2绝对值-num1绝对值return single_minus(num2, num1);}}else if(flag_2 == -1){//cout << "situation 2" << endl;num2 = num2.substr(1);if(compare(num1, num2)){// 正绝对值比负数大 num1绝对值-num2绝对值return single_minus(num1, num2);}else{//cout << "situation 2" << endl;string fret = "-";return fret + single_minus(num2, num1);// 正绝对值比负数小 num2绝对值-num1绝对值,返回值符号为负}}}else if(flag_1 == -1){// 两个数字全部都是负数num1 = num1.substr(1);num2 = num2.substr(1);string ret = single_add(num1, num2);return "-" + ret;}else{return single_add(num1, num2);}}// 比较两个字符串数字的大小bool compare(string num1, string num2){//cout << num1 << " " << num2 << endl;int pos_1 = 0;int pos_2 = 0;for(int i=0; i<num1.size(); i++){if(num1[i] != '0' || i==num1.size()-1){pos_1 = i;break;}}for(int i=0; i<num2.size(); i++){if(num2[i] != '0' || i==num2.size()-1){pos_2 = i;break;}}if(num1.size() - pos_1 > num2.size() - pos_2){return true;}else if(num1.size() - pos_1 < num2.size() - pos_2){return false;}for(int i=pos_1, j=pos_2; i<num1.size(); i++, j++){if(num1[i] == num2[j]){continue;}else if(num1[i] > num2[j]){return true;}else{return false;}}return true;}// 纯正数或者负数相加string single_add(string num1, string num2){int i = num1.length() - 1, j = num2.length() - 1, add = 0;string ans = "";while (i >= 0 || j >= 0 || add != 0) {int x = i >= 0 ? num1[i] - '0' : 0;int y = j >= 0 ? num2[j] - '0' : 0;int result = x + y + add;ans.push_back('0' + result % 10);add = result / 10;i -= 1;j -= 1;}// 计算完以后的答案需要翻转过来reverse(ans.begin(), ans.end());//cout << num1 << " " << num2 << " " << "add_ans:" << ans << endl;return ans;}string generate_zeros(int num){string ret = "";for(int i=0; i<num; i++){ret += "0";}return ret;}// 前面的数字减去后面的数字,然后填充到原本的长度string single_minus(string num1, string num2){int i = num1.size() - 1, j = num2.size() - 1, minus = 0;string ans = "";while(i >=0 || j >=0){int x = i >= 0 ? num1[i] - '0' : 0;int y = j >= 0 ? num2[j] - '0' : 0;int result = minus + x - y;if(result < 0){result = result + 10;minus = -1;}else{minus = 0;}ans.push_back('0' + result);i -= 1;j -= 1;}reverse(ans.begin(), ans.end());//cout << num1 << " " << num2 << " " << "minus_ans:" << ans << endl;return ans;}string to_odd_string(string num, int maxsize){for(int i=num.size(); i<maxsize; i++){num = "0" + num;}return num;}// 删除当前数字前的所有零string cut_zero(string num){int flag = 1, pos = 0, beginer = 0;if(num[0] == '-'){flag = -1;beginer = 1;}if(num == "0"){return "0";}for(int i=beginer; i<num.size(); i++){if(num[i] != '0'){pos = i;break;}}num = num.substr(pos);if(flag == -1){string f = "-";num = f + num;}return num;}string num2str(int i){stringstream ss;ss<<i;return ss.str();}int str2num(string s){int num;stringstream ss(s);ss>>num;return num;}bool judge_zero(string num){for(int i=0; i<num.size(); i++){if(num[i] != '0'){return false;}}return true;}
};int main(){Solution sl;string a, b;while(true){cin >> a >> b;cout << sl.multiply(a, b) << endl;}//cout << sl.single_minus("26191783350166500000000000000000", "8940136631827500000000");//cout << sl.judge_zero("0000000") << endl;return 0;
}

分治法求解大整数乘法相关推荐

  1. PYTHON:大整数乘法(分治法)

    何为分治法: 分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同.求出子问题的解,就可得到原问题的解.即一种分目标完成程序算法,简单问题可用二分法 ...

  2. 大整数相乘java_大整数乘法—java实现

    大整数相乘 参考博客: https://blog.csdn.net/oh_maxy/article/details/10903929 https://blog.csdn.net/u010867294/ ...

  3. 分治法实验之大整数乘法(算法设计分析)

    分治法实验之大整数乘法 01. 问题描述 02. 输入格式 03. 输出格式 04. 输入样例 05. 输出样例 06. 问题分析 07. 算法设计 08. 代码实现 09. 测试结果 10. 复杂度 ...

  4. 大整数乘法(分治法)

    大整数乘法(分治法) 题目描述:设X和Y都是n位的十进制整数,计算它们的乘积X*Y. 如果按照我们日常的计算方法,应该就是将两个数逐位相乘,最后加起来得到最终的结果,时间复杂度为O(n2); 因此我们 ...

  5. 大整数乘法(递归+分治法)

    目录 一.问题描述 二.思路分析 分治法介绍: 问题分析: 三.算法伪代码 四.代码实现效果 五.源代码 六.参考文章 一.问题描述 请设计一个有效的算法,可以进行两个n位大整数的乘法.(n=2^k, ...

  6. python两数相乘代码_Python 实现大整数乘法算法的示例代码

    我们平时接触的长乘法,按位相乘,是一种时间复杂度为 O(n ^ 2) 的算法.今天,我们来介绍一种时间复杂度为 O (n ^ log 3) 的大整数乘法(log 表示以 2 为底的对数). 介绍原理 ...

  7. 【分治算法】大整数乘法

    前言 最近开了算法导论课,上来就是递归分治,大整数乘法就是分治法的典型案例,通过参考网上书上我终于编程实现了大整数乘法,特此纪念 原理 由于两个大整数直接相乘太大,所以我们可以将它划分成几个小块分别相 ...

  8. 【分治】大整数乘法(C++)

    刚发现个问题,,初学时竟然是使用的long型进行计算的,导致这篇文章虽展现了分治的想法,但并没有实际解决大整数乘法的计算问题. 仅供参考,日后再改 一.大整数乘法 一般计算方法 有n位大整数X和Y,计 ...

  9. 分治法 分治法求解递推式

    分治法 分治法基本就是下面的三步 分(divide):无法有效解决的划分更小的问题 治(conquer):递归求每一个子问题的解 合(combine):合并解得出原问题解 MergeSort:排列 1 ...

最新文章

  1. apache log4j-1.2.15的使用
  2. SQL語句大全4(常用函數)
  3. 解决wamp、vertrigo等集成环境安装后apache不能启动的问题
  4. How Many Replication Method In SAP HANA
  5. @async注解_SpringBoot中Async异步方法和定时任务介绍
  6. JS实现星星评分功能实例代码(两种方法)
  7. 决策树——CART和模型树
  8. 不用long的危害(记洛谷P5534题的WA经历,Java语言描述)
  9. 企业域名更换操作系列4:下载旧域域信息配置
  10. eclipse 左边目录结构下五referenced library解决办法
  11. 2017.4.24 聪明的质检员 思考记录
  12. MTKI 驱动(57)---音频参数含义
  13. .net伪静态传多个参数
  14. LeetCode从读题到自闭:1. 两数之和
  15. 腾讯QQ for linux告别pre,开始beta
  16. displayTag使用详解
  17. python 批量下载地理空间数据云
  18. 阿里王坚:万物互联网=云计算+大数据
  19. 树莓派获取root权限
  20. 微信小程序音乐播放器源码【包调试运行】

热门文章

  1. 【Revit二次开发】标高
  2. 双指缩放canvas图片_小程序实现图片双指缩放
  3. JavaWeb与Tomcat简介
  4. 均衡之刃这款球拍是谁创作的
  5. 用C++,打印*号菱形图形
  6. Qt QTableWidget类方法setItem()bug问题解决
  7. python使用localStorage.setItem()写入本地存储转义符的问题
  8. python 乡镇轮廓 高德_Python调用高德地图API爬取经纬度
  9. STM32-MDK V5 生成bin文件和hex文件
  10. QUIC协议(握手过程)简要介绍