超长整数的加减乘除四则运算

超长整数四则运算

  • 概要
    • 除法
    • 加法
    • 减法
    • 乘法


前面写了减法和加法,这里做个汇总,写一下四则运算的分别实现

概要

当参加运算的数字是超过long型长度的数据,以至于只能用string字符串来存储,这时候加减乘除的运算已经不能用了。

对于这种问题Java提供了一个BigInteger类,可以用来存储,并有对应的加减乘除的方法,要求两个操作数都必须是BIgInteger型。可以解决问题。

这里从模拟计算机的内部实现解读来解决问题。

这个时候就可以像小时候列竖式来计算一样,一位一位的计算,或进位或借位,模拟计算的过程。将整个的计算拆解成一个个小的计算过程。

除法与加减和乘有所不同,除法是从高位到低位进行计算,加减乘是从低位到高位进行计算。

除法

被除数 A ÷ 除数 B = 商 C ...余数 D

思路是:

  1. 取 A 第一位(从高到低),除以除数,得到商和余数,
  2. 商加入一个数组,余数乘10+被除数的下一位,除以除数,得到商和余数,
  3. 依次执行 2,直到将A的每一位都遍历完。

代码实现:

//这里的在控制台输入一个长整数被除数和一个短int型除数
//这只是我可以人为的改变输入
//没有给定一个值,采用了BufferReader和StringBuilder来缓存读入和类对象加快处理速度

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
  public static void main(String[] args) throws IOException {
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    String[] ab = bufferedReader.readLine().split(" ");
    bufferedReader.close();
    int b = Integer.parseInt(ab[1]), t = 0;
    StringBuilder stringBuffer = new StringBuilder();
    for (int i = 0; i < ab[0].length(); i++) {
      t = t * 10 + ab[0].charAt(i) - '0';
      stringBuffer.append((char)(t / b + '0'));//商加入字符串输出
      t = t % b;//计算余数进行下一轮运算
    }
    String result = stringBuffer.toString();
    if (result.charAt(0) == '0'  && result.length() != 1) {
    //若第一次运算的商是不足除的,则为零,这时候要把这个零舍掉
      System.out.print(result.substring(1) + " " + t);
    } else {
      System.out.print(result + " " + t);
    }
  }
}
输入样例:
123456789050987654321 7

输出样例:
17636684150141093474 3

加法

同号相加完成后打印符号就可以,这里做的是正数相加。若要判断符号加几行代码就行。

若是异号相加,等同于减法,在后面。

由于加减乘法是从低位开始计算的,所以我们需要一个倒序的操作(也可以不导致,但是容易混淆)

思路是

  1. 将把整数倒序存储,整数的个位存于数组0下标位置,最高位存于数组长度-1下标位置。之所以倒序存储,更加符合我们从左到右访问数组的习惯。
  2. 创建结果数组,结果数组的最大长度是较大整数的位数+1,原因很明显,可能会存在进位导致长度加一。
  3. 遍历两个数组,从左到右按照对应下标把元素两两相加,不满10直接在当前数组位置保存(当前数组位置的值 += 计算后的值),满10减10后存在对应的位置,进一位,填充1到下一个数组位置中。
  4. 把Result数组的全部元素再次逆序,若有0,去掉首位,0,就是最终结果。

代码实现

public class Main {
    public static String bigNumberSum(String bigNumberA, String bigNumberB) {

        //1.把两个大整数用数组逆序存储
        char[] charsA = new StringBuffer(bigNumberA).reverse().toString().toCharArray();
        char[] charsB = new StringBuffer(bigNumberB).reverse().toString().toCharArray();

        //2.构建result数组,数组长度等于较大整数位数+1
        int maxLength = charsA.length > charsB.length ? charsA.length : charsB.length;
        int[] result = new int[maxLength + 1];
        //3.遍历数组,按位相加
        for (int i = 0; i < result.length; i++) {
            int temp = result[i];
            if (i < charsA.length) {
                temp += charsA[i] - '0';
            }
            if (i < charsB.length) {
                temp += charsB[i] - '0';
            }

        //判断是否进位
            if (temp >= 10) {
                temp = temp - 10;
                result[i + 1] = 1;
            }
            result[i] = temp;
        }

        //4.把result数组再次逆序并转成String
        StringBuilder sb = new StringBuilder();
        // 判断最后一位是否发生了进位
        int head=result.length-1;
		 if (result[result.length-1] == 0 ) 
			 head=result.length-2;
        for (int i = head; i >= 0; i--) 
			sb.append(result[i]);
        return sb.toString();
    }

    public static void main(String[] args)
    {
        System.out.println(bigNumberSum("426709752318", "95481253129"));
    }
}

减法

减法需要判断两个数的大小和正负,大小可以通过长度和对应位的大小来比较,长度不同,长的 数字大,相同长度,对应位比较,第一次出现较大的数是大的。则用他的符号。同时,负数-正数可以当做加法来算。流程同上,只不过有符号的判断,这里不考虑。

因操作有点麻烦,这里默认是全正数且大数减小数

思路是

  1. 将把整数倒序存储,整数的个位存于数组0下标位置,最高位存于数组长度-1下标位置。之所以倒序存储,更加符合我们从左到右访问数组的习惯。
  2. 创建结果数组,结果数组的最大长度是较大整数的位数,不需要考虑加一的情况(负-正不考虑)。
  3. 遍历两个数组,从左到右按照对应下标把元素两两相减,够减直接在当前数组位置保存(当前数组位置的值 += 计算后的值),不够减按10减,借一位,第一个数字下一位的值减一。
  4. 把Result数组的全部元素再次逆序,若有0,去掉首位,0,就是最终结果。

代码实现

package PATY;
public class BigNumberSub {
    public static String bigNumberSum(String bigNumberA, String bigNumberB) {

        //1.把两个大整数用数组逆序存储
        char[] charsA = new StringBuffer(bigNumberA).reverse().toString().toCharArray();
        char[] charsB = new StringBuffer(bigNumberB).reverse().toString().toCharArray();

        //2.构建result数组,数组长度等于较大整数位数+1
        int maxLength = charsA.length > charsB.length ? charsA.length : charsB.length;
        int[] result = new int[maxLength + 1];
        //3.遍历数组,按位相减
        int[] need = new int[maxLength + 2];//判断是否需要借位
        for (int i = 0; i < result.length; i++) {
            int temp = 0;
            int a=0,b=0;
            if (i < charsA.length) {
                if(need[i]==1)
                    a= charsA[i] - '0'-1;
                a= charsA[i] - '0';
            }
            if (i < charsB.length) {
                b= charsB[i] - '0';
            }

        //判断是否借位
            if (a<b) {
                temp = a+10-b;
                need[i+1]=1;
            }else
                temp=a-b;
            result[i] = temp;
        }

       //4.把result数组再次逆序并转成String
        StringBuilder sb = new StringBuilder();
        boolean flag = true;
        for (int i = result.length - 1; i >= 0; i--) {
            //判断最后一位是否发生了进位
            if (result[i] == 0 && flag) {
                flag = false;
                continue;
            }

            sb.append(result[i]);
        }
        return sb.toString();
    }

    public static void main(String[] args)
    {
        System.out.println(bigNumberSub("222222222", "1111"));
    }
}

乘法

乘法与加法类似,不过有些复杂,两个for循环,分别是两个数字的位数长度作为范围,两个循环依次遍历,这里进位就变成了要对10取商进位,对商取余保存在当前位。思路大概就是这样。
emmmm,代码懒的写了,借鉴网上找的代码,有些bug,调通了贴出来。

这个代码一开始没有进行倒置的操作,而是从后向前读取,效果一样。

思路是

  1. 创建结果数组,结果数组的最大长度是两个数字的长度相加 l+r。
  2. 遍历两个数组,从一个数的最低位开始,依次乘第二个数字(从低位到高位),加上进位和这个数组位置本来的数(因为乘法,一个数组位置可能会有多次计算),对10取余数得到新的当前数组位置的数,取商得到新一轮的进位。
  3. 把Result数组的全部元素再次逆序,若有0,去掉首位,0,就是最终结果。

代码实现


public class BigNumberMul {
    public static String multiply(String num1, String num2) {
        int l = num1.length();
        int r = num2.length();
        //用来存储结果的数组,可以肯定的是两数相乘的结果的长度,肯定不会大于两个数各自长度的和。
        int[] num = new int[l+r];//从低到高存贮,最后需倒置
        //第一个数按位循环
        for(int i=0;i<l;i++) {
            //得到最低位的数字
            int n1=num1.charAt(l-1-i)-'0';
            //保存进位
            int tmp=0;
            //第二个数按位循环
            for(int j=0;j<r;j++) {
                int n2=num2.charAt(r-1-j)-'0';
                //拿出此时的结果数组里存的数+现在计算的结果数+上一个进位数
                tmp=tmp+num[i+j]+n1*n2;
                //得到此时结果位的值
                num[i+j]=tmp%10;
                //此时的进位
                tmp/=10;
            }
            //第一轮结束后,如果有进位,将其放入到更高位
            num[i+r]=tmp;
        }

        int i=l+r-1;
        //计算最终结果值到底是几位数,高位的零需要舍去(因为可能达不到l+r的长度)
        while(i>0&&num[i]==0){
            i--;
        }
        StringBuilder result=new StringBuilder("");
        while(i>=0) {
            result.append(num[i--]) ;
        }
        return result.toString();
    }

    public static void main(String[] args)
    {
        System.out.println(multiply("111", "1111"));
    }
}

你可能感兴趣的:(Java)