这里主要用到两个函数,moveFrontZero()用来去除前面的0且一般用在最后将要输出结果的时候,align()用来给短的数字补0至两个数有相同位数(相同长度)。
/** 去除前面的0 */
void moveFrontZero(string &res){
res.erase(0, res.find_first_not_of('0'));
if(res.empty()) res = "0";
}
/** 补位对齐并返回最长的长度 */
int align(string &a, string &b){
while(a.length() < b.length()) a = "0" + a;
while(b.length() < a.length()) b = "0" + b;
return a.length();
}
从最低位(个位)开始对应相加,超过10就进位+1,通过拼接字符串形成计算结果。
可对应洛谷题:洛谷题
string add(string &a, string &b){
string res;
int len = align(a, b);
for(int i = len-1; i >= 0; i--){
int num1 = a[i] - '0';
int num2 = b[i] - '0';
if(num1 + num2 > 9 && i != 0){
a[i-1] = a[i-1] - '0' + 1 + '0';
res = to_string(num1 + num2 - 10) + res; //字符串拼接
}
else res = to_string(num1+num2) + res;
}
moveFrontZero(res);
return res;
}
从低位开始对应相减,不够减就从借位减一。这里实现的还不是很优(有待优化),因为有个前提——两个数都必须是整数且不是负数。
string sub(string &a, string &b){ //subduction
string res;
int len = align(a, b);
bool flag = false; //用来判断最后结果是否为负数
if(a<b){
flag = true;
a.swap(b); //确保a是最大数
}
for(int i = len-1; i >= 0; i--){
int num1 = a[i] - '0';
int num2 = b[i] - '0';
if(num1 < num2){
// -'0'-1是为了借位,+'0'是为了还原成char类型字符
a[i-1] = a[i-1]-'0' - 1 + '0';
res = to_string(num1+10 - num2) + res;
}else{
res = to_string(num1 - num2) + res;
}
}
moveFrontZero(res);
if(flag) res = "-" + res;
return res;
}
主要思想是把乘法转换成加法运算,分割相对小的数,如:
12345×24 可分割24成4个12345相加,20个123456相加(也就是2个123450相加),即 12345×24 = 4×12345 + 2×123450
/**
* 版本一:通过string处理
*/
string mul(string &a, string &b){
string res = "0";
align(a, b);
if(a < b) a.swap(b); //固定:a永远是最大的,b永远是最小的
for(int i = b.size()-1; i >= 0; i--){
int num1 = b[i] - '0';
if(i != b.size()-1) a = a + "0";
for(int j = 0; j < num1; j++)
res = add(res, a);
}
moveFrontZero(res);
return res;
}
/**
* 版本二:通过vector处理
*/
vector<int> mul(vector<int> a, int b){
vector<int> c;
int t = 0; //进位
for(int i = 0; i < a.size(); i++){
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while(y){
c.push_back(t % 10);
t /= 10;
}
return c;
}
int main(){
...
vector<int> res;
res.push_back(1);
...
for(int i = res.size()-1; i >= 0; i--)
printf("%d", res[i]);
puts("");
return 0;
}
看下图数据更易懂~
首先初始化
循环加上4次加上12345后结果res=49380
重复,循环2次加上123450,res=296280
这里用的是算商数、余数的除法,而直接除的算法有待研究和优化。
链接:大数除法
下面附上链接里算法的修正版式
string div(string &a, string &b){
string res;
stringstream sstream; //定义个字符串流
//被除数 除数 余数
int dividend, divisor = stoi(b), remainder = 0, i;
for(i = 0; i < a.length()-1; i++){
dividend = (a[i] - '0') + remainder*10;
if(dividend >= divisor){
sstream << dividend / divisor;
remainder = dividend % divisor;
}
else{
//数字的首位小于除数时,不打印0
if(i != 0) sstream << "0";
remainder = dividend;
}
}
dividend = (a[i] - '0') + remainder*10; //处理最后一位数
string temp;
sstream >> temp;
res = temp + to_string(dividend / divisor) + "......" + to_string(dividend % divisor);
moveFrontZero(res);
return res;
}
题目链接:题目跳转