大数问题
主要是旨在当超过int longlong范围内的大型数目,如何通过数组存储和逐位计算,来解决相关的实际问题;
主要研究的方向有四个:高精度加法、高精度减法、低精度乘法、低精度除法;
这里注意下,高精度值是指两个加数或者减数都为大数数组,低精度是指一个是正常整型数,一个是大数;
一、对于存储结构:
struct bign { int len; int num[10000]; bign() { len = 0; fill(num, num + 10000, 0); } };
具有标记统计位,也就是数的位数len,存储数字字符的num[10000];
这里值得注意的是:
1.采用存储数的数组,最好别用vector,不然不利于加减运算,来回push_back很讨厌;
2.对于数的存储,为了方便遍历,最好逆向存储,也就是低位数字放在低位索引,高位数字放在高位索引;
二、高精度加法计算:
相当于模仿了竖式计算,需要注意进位问题;
bign add(bign a, bign b) { bign c; int carry = 0;//carry位代表进位; for (int i = 0; i < a.len || i < b.len; i++) { //这里注意下索引问题,由于高位为0,所以不用担心两个位数不同的数字嘉禾的问题,相当于0加上一个数加caryy位; int temp = a.num[i] + b.num[i] + carry; c.num[c.len++] = temp % 10; //同时统计c.len到底有多少位; carry = temp % 10; } if (carry != 0) { c.num[c.len++] = carry; //当a,b两个大数范围相同时,存在进位要在加一位; } return c; }
三、高精度除法:
bign sub(bign a, bign b) {
bign c;
for (int i = 0; i < a.len || b.len; i++) {
//在输入计算前一定要注意,保证a代表的整数大于b代表的整数,如果是负数计算,直接调换位置运算结果加负好;
if (a.num[i] < b.num[i]) {
a.num[i] += 10;
a.num[i + 1] --;
}
c.num[c.len++] = a.num[i] - b.num[i];
}
while (c.len-1>=1&&c.num[c.len-1]==0){
c.len--;
}
return c;
}
这里仍要注意两个可能的抑或点:
1.之前自己在想是否可能存在a<b,从而使得a-b某一位向高位借位,会存在高位由0变成-1的情况,如果按照示例代码直接进行大小判断,可以解决这个问题;
2.例如,当62-61=1,如果按照上述计算,会存在c.len=2的情况,所以要进行高位前导零的去除,c有可能满足c=0000....000x,所以要循环将前导0去除;
四、低精度乘法:
如果仍然按照逐个数位进行乘积,可能仍然会遇到很多问题,很繁琐;
这里示例代码给出的方式是把乘数看作为一个整体,直接整体挨个乘,一次进行计算,和加法类似,多的高位作为carry位直接参与下一次计算;
bign multi(bign a, int b) {
bign c;
int carry = 0;
for (int i = 0; i < a.len; i++) {
int temp = a.num[i] * b + carry;
c.num[c.len++] = temp % 10;
carry = temp / 10;
}
while (carry!=0){
c.num[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
五、低精度除法:
bign divide(bign a, int b, int& r) { bign c; c.len = a.len; for (int i = a.len - 1; i >= 0; i--) { r = r * 10 + a.num[i]; if (r < b) c.num[i] = 0; else { c.num[i] = r / b; r = r % b; } } while (c.len - 1 >= 1 && c.num[c.len - 1] == 0) { c.len--; } return c; }
这里可以发现两个规律:
1.如果不忽略前导零的情况下,商的位数应该和被除数的位数相同。所以要在最后一步操作进行去除前导零的操作;
2.整体商的计算可以说是围绕着余数来进行的,余数为上一位的残留位*10+本位,在看和除数相除够不够除,够的话商,不够的话直接商0;

浙公网安备 33010602011771号