简体   繁体   中英

Big numbers multiplication algorithm doesn't always work

I'm trying to create a function that reads two positive natural numbers from an input file and, once calculated, writes their product in an output file.
The numbers are "big numbers", they can not be stored in a variable such as int or long so I want to store their digits in an array.

I wrote the following code that seems to work with some numbers but fails when I insert for example

55545252575453525348514954525256515056485755545157575748525349575251565256515554545052494854495550525752505756 * 50485456554954525356575657575048555450535257534951545354504957495454525448485751565254525155515656575655535754

that should be

2804227435732034912849259183651914853320425721942955023578511952364152651715628400479161029284275992691222252865738869930419556645027062168262691246978645407150215825953157986304093759319409280056139599351010978148800024

I get

2804227435732034912838248072540803742208304509820742801356289729131829419382295067135726685849922558246677798320394335486985213210683628834929357923746322174827983613731945865091982648208298268945129498351010978148800024

This is the code:

public static void multiplication(String inFileName, String outFileName) {
        int[] a, b, res; //a is the first number, b is the second number, res is the final result
        int a_i, b_i; //indexes to navigate inside the numbers

        a = readBigNumber(inFileName, 0); //This works
        if(a == null) return;
        System.out.print("*** a: "); printIntArray(a); System.out.println("");  //DEBUG

        b = readBigNumber(inFileName, 1); //This works
        if(b == null) return;
        System.out.print("*** b: "); printIntArray(b); System.out.println("");  //DEBUG

        res = new int[a.length + b.length + 1]; //The result cannot be longer than this
        Arrays.fill(res, 0); //Fill the array with zeros

        //if the length of a < length of b, swap
        int[] tempArr;
        if (a.length < b.length) {
            tempArr = new int[a.length];
            System.arraycopy(a, 0, tempArr, 0, a.length);
            a = new int[b.length];
            System.arraycopy(b, 0, a, 0, b.length);
            b = new int[tempArr.length];
            System.arraycopy(tempArr, 0, b, 0, tempArr.length);
        }

        //this algorithm works like the "manual" column multiplication
        int temp;
        int res_i = 0; //index to navigate in the res array
        int carry = 0;
        for(b_i = b.length-1; b_i >= 0; b_i--) {
            for(a_i = a.length-1; a_i >= 0; a_i--) {
                temp = a[a_i] * b[b_i] + carry; //save the product in a temp variable
                res_i = res.length - 1 - (a.length - 1 - a_i) - (b.length - 1 - b_i); //calculate the index in the res array
                //I need to have just one digit [0-9]
                if(temp > 9) { //If temp has more than one digit, take the right-one and put the left-one in the carry
                    res[res_i] += temp % 10; //right-digit
                    carry = (temp / 10) % 10; //left-digit
                } else {
                    res[res_i] += temp;
                    carry = 0;
                }
            }

            //when I exit the a-loop, if the carry is not 0, I have to put it in the result before continuing
            if(carry > 0) {
                res[res_i - 1] += carry;
                carry = 0;
            }
        }

        //Once completed, each array cell could have more than one digit.
        //Check it right to left and if so, keep the right-digit e sum the left-digit in the left cell
        for(int i = res.length-1; i >= 0; i--) {
            if(res[i] > 9) {
                res[i - 1] += (res[i] / 10) % 10; //left-digit
                res[i] = res[i] % 10; //right-digit
            }
        }

        //Write the final result
        String res_str = "";
        int start_i = 0;
        while(start_i < res.length && res[start_i] == 0) start_i++; //Ignore initial zeros
        if(start_i == res.length) {
            writeBigNumber(outFileName, "0"); //I checked the whole array, the product is 0
        } else {
            for(int i = start_i; i < res.length; i++) {
                res_str += String.valueOf(res[i]);
            }
            writeBigNumber(outFileName, res_str);
        }

        System.out.println("*** res: " + res_str);  //DEBUG
    }

These are some "helper functions":

//This works
private static int[] readBigNumber(String inFileName, int lineIndex) {
        File inputFile = new File(inFileName);
        Scanner scan = null;
        int i = 0;
        int[] num;
        char[] rawInput;

        try {
            scan = new Scanner(inputFile);
            while(scan.hasNextLine()) {
                rawInput = scan.nextLine().toCharArray();
                if(i == lineIndex) {
                    num = new int[rawInput.length];
                    for(int j = 0; j < num.length; j++) {
                        num[j] = rawInput[j] - '0';
                    }
                    scan.close();
                    return num;
                }
                i++;
            }
        } catch (Exception e) {
            System.out.println("An error occurred while reading the numbers from file: ");
            e.printStackTrace();
        } finally {
            if(scan != null)
                scan.close();
        }

        return null;
    }

    private static void writeBigNumber(String outFileName, String num) {
        try {
            File outputFile = new File(outFileName);
            FileWriter writer = new FileWriter(outputFile, false);
            writer.write(num);
            writer.close();
        } catch (Exception e) {
            System.out.println("An error occurred while writing the result: ");
            e.printStackTrace();
        }
    }

I know that there are many other algorithms that do the same thing and are a lot better than mine, but I want to understand why this doesn't always work. Thanks in advance.

Your bug is in the part where you are doing the final carry-over from cells that have numbers with more than one digit in them:

        if(res[i] > 9) {
            res[i - 1] += (res[i] / 10) % 10; //left-digit
            res[i] = res[i] % 10; //right-digit
        }

Here you are assuming that res[i] has at most two digits (isn't greater than 99). The assumption is not sound. I tried multiplying 99 999 999 999 (eleven digits) with itself using your program, and res[12] contained 100 at this point. So (res[i] / 10) % 10 evaluates to 0 and you are not carrying anything over to res[11] or res[10] as you should.

I suppose I should leave it to yourself to find a good fix.

Edit: For the readability, this is your own fix from your comment, formatted and indented:

    for (int i = res.length - 1; i >= 0; i--) {
        if (res[i] > 9) {
            if (res[i] > 99) {
                res[i - 1] += ((res[i] / 100) % 10) * 10; // first digit (*10)
                res[i - 1] += (res[i] / 10) % 10; // second digit
            } else {
                res[i - 1] += (res[i] / 10) % 10; // left-digit
            }
            res[i] = res[i] % 10; // right-digit
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM