第十章分治算法(大数相乘)

分治算法:分治算法由两部分组成,分和治,分是使问题规模变小,递归解决较小的问题,治是从子问题的解中构建原问题的解。
传统上,在正文中至少含有两个递归调用的例程叫做分治算法,而正文中只含有一个递归调用的例程不是分治算法。
我们一般坚持子问题是不相交的。
在前面写的归并排序和快速排序就是分治算法的一种例程。
所有有效的分治算法都是把问题分成一些子问题,每个子问题都是原问题的一部分,然后进行某些附加的工作,以算出最后的答案。

分治算法解决大整数相乘。
如果X = 61438521,而Y = 94736407,那么XY = 5820464730934047
如果把x和y拆成两半,分别由最高几位和最低几位数字组成。
XL = 6143,XR = 8521,YL=9473,YR = 6407.

则 X = XL10⁴ +XR,Y = YL10⁴ + YR。
则 XY = XLYL10⁸ + YL10⁴XR + XL10⁴YR + XRYR
则 XY = XLYL
10⁸ + (XRYL+XLYR)10⁴+ XRYR
则 XY = XLYL*10⁸ + ((XL+XR)(YR+YL)-XLYL-XRYR)10⁴+ XRYR
则 XY可以由3次乘法组成,即 XLYL,XRYR,(XL+XR)
(YR+YL)
它们每一个都是原来问题大小的一半(N/2),10⁸和10⁴作乘法实际就是添加一些0。

核心算法

string multiply(string &x, string &y) {
	bool flag = false;						//最终结果的正负号,0为正号,1为负号
	if (x.at(0) == '-' && y.at(0) !='-' || x.at(0) != '-' && y.at(0) == '-') {
		//一正一负,最终符号为负
		flag = true;
	}
	if (x.at(0) == '-') {		
		//去除负号
		x = x.substr(1);
	}
	if (y.at(0) == '-') {
		//去除负号
		y = y.substr(1);
	}
	int init_len = 4;
	if (x.length() > 2 || y.length() > 2) { // 长度大于2时,最小长度应该为4,故初始值为4
		if (x.length() >= y.length()) {
			while (init_len < x.length())init_len *= 2; //计算两个数最终的长度
			//添加前置0
			if (x.length() != init_len) {
				addPreZero(x, init_len - x.length());
			}
			addPreZero(y, init_len - y.length());
		}
		else {
			while (init_len < y.length())init_len *= 2;
			//添加前置0
			addPreZero(x, init_len - x.length());
			if (y.length() != init_len) {
				addPreZero(y, init_len - y.length());
			}
		}
	}
	int n = x.length();
	string result;
	if (n <= 2) { //长度为2时,结束递归
		int x1 = strToInt(x);
		int y1 = strToInt(y);
		int z = x1 * y1;
		result = intToStr(z);
	}
	else {
		string xl, xr, yl, yr;
		xl = x.substr(0, n / 2);
		xr = x.substr(n / 2, n);
		yl = y.substr(0, n / 2);
		yr = y.substr(n / 2, n);
		string xlyl = multiply(xl, yl);
		string xryr = multiply(xr, yr);
		string xlAddXr = add(xl, xr);
		string ylAddYr = add(yl, yr);
		string t = add(xlyl, xryr);
		string t1 = multiply(xlAddXr, ylAddYr);
		string t2 = subtract(t1, t);
		addLastZero(t2, n / 2);
		addLastZero(xlyl, n);
		result = add(add(t2, xlyl), xryr);
	}
	if (flag) { //结果为负数
		result.insert(0, "-");
	}
	return result;
}

其它函数

#include <iostream>
#include <malloc.h>
#include <algorithm>
#include <sstream>
using namespace std;

int strToInt(string k) {
	//string字符串变整数型
	int back;
	stringstream instr(k);
	instr >> back;
	return back;
}
string intToStr(int intValue) {
	string result;
	stringstream stream;
	stream << intValue;//将int输入流
	stream >> result;//从stream中抽取前面放入的int值
	return result;
}
void removePreZero(string &str) {
	//去掉前置0,需要考虑只有一个0或者全部是0的情况
	if (str.length() == 1)return;
	int k = 0;
	for (int i = 0; i < str.length(); i++) {
		if (str.at(i) == '0') {
			k++;
		}
		else {
			break;
		}
	}
	if (k == str.length())str = "0";
	else {
		str = str.substr(k);
	}
}
//大数相加,不考虑前置0的情况
string add(string x, string y) {
	string result = "";
	//去掉前置0
	removePreZero(x);
	removePreZero(y);
	//反转字符串方便相加
	reverse(x.begin(), x.end());
	reverse(y.begin(), y.end());
	for (int i = 0, j = 0; j || i < max(y.length(), x.length()); i++) {
		int t = j;
		if (i < x.length())t += (x.at(i) - '0');
		if (i < y.length())t += (y.at(i) - '0');
		int q = t % 10;
		result.insert(0, intToStr(q));
		j = t / 10;
	}
	return result;
}
string subtract(string &x, string &y) {
	string result = "";
	//去掉前置0
	removePreZero(x);
	removePreZero(y);
	int len_x = x.length();
	int len_y = y.length();
	int len = len_x > len_y ? len_x : len_y;
	int *tempResult = (int *)malloc(sizeof(int) * len);
	string sign;
	if (len_x > len_y) {// x - y为正数
		sign = "+";
	}
	else if (len_x < len_y) { //x-y为负数
		sign = "-";
	}
	else { //长度相同则判断各位的大小
		int i;
		for (i = 0; i < len_x; i++) {
			if (x.at(i) == y.at(i))continue;
			else if (x.at(i) > y.at(i)) {
				sign = "+";
				break;
			}
			else {
				sign = "-";
				break;
			}
		}
		//说明没有break,说明x == y
		if (i == len_x) {
			return "0";
		}
	}
	//反转字符串方便相减
	reverse(x.begin(), x.end());
	reverse(y.begin(), y.end());
	int q = 0;
	//若x大,则直接相减,否则用y-x,并且最终加上负号
	for (int i = 0; i < len; i++) {
		int aint = i < len_x ? x.at(i) - '0' : 0;
		int bint = i < len_y ? y.at(i) - '0' : 0;
		if (sign.at(0) == '+') {
			tempResult[q++] = aint - bint;
		}
		else {
			tempResult[q++] = bint - aint;
		}
	}
	for (int i = 0; i < q; i++) {
		if (tempResult[i] < 0) {
			tempResult[i + 1] -= 1;
			tempResult[i] += 10;
		}
	}
	q--;
	while (tempResult[q] == 0)q--;
	for (int i = q; i >= 0; i--) {
		result += intToStr(tempResult[i]);
	}
	if (sign.at(0) == '-')return sign + result;
	return result;
}
//添加前置0
void addPreZero(string &str, int zero_num) {
	for (int i = 0; i < zero_num; i++)
		str.insert(0, "0");
}
//添加后置0
void addLastZero(string &str, int zero_num) {
	for (int i = 0; i < zero_num; i++)
		str += "0";
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值