大数问题

主要是旨在当超过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;

posted @ 2020-03-05 10:22  暮云林凌  阅读(367)  评论(0)    收藏  举报