java - 大数乘法算法并不总是有效

标签 java multiplication bignum

我正在尝试创建一个函数,该函数从输入文件中读取两个正自然数,并在计算后将其乘积写入输出文件中。
这些数字是“大数字”,它们不能存储在 intlong 等变量中,所以我想将它们的数字存储在数组中。

我编写了以下代码,该代码似乎适用于某些数字,但在插入时失败

55545252575453525348514954525256515056485755545157575748525349575251565256515554545052494854495550525752505756 * 50485456554954525356575657575048555450535257534951545354504957495454525448485751565254525155515656575655535754

应该是

2804227435732034912849259183651914853320425721942955023578511952364152651715628400479161029284275992691222252865738869930419556645027062168262691246978645407150215825953157986304093759319409280056139599351010978148800024

我明白

2804227435732034912838248072540803742208304509820742801356289729131829419382295067135726685849922558246677798320394335486985213210683628834929357923746322174827983613731945865091982648208298268945129498351010978148800024

这是代码:

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
    }

这些是一些“辅助函数”:

//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();
        }
    }

我知道有许多其他算法可以做同样的事情,并且比我的算法好很多,但我想了解为什么这并不总是有效。提前致谢。

最佳答案

您的错误在于您对包含多于一位数字的数字的单元格进行最终结转的部分:

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

这里假设 res[i] 最多有两位数字(不大于 99)。这个假设并不合理。我尝试使用您的程序将 99 999 999 999(十一个数字)与其自身相乘,此时 res[12] 包含 100。因此,(res[i]/10) % 10 的计算结果为 0,并且您没有将任何内容传递给 res[11]res[10] 正如你应该的那样。

我想我应该让你自己找到一个好的解决方案。

编辑:为了便于阅读,这是您自己根据评论进行的修复,已格式化并缩进:

    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
        }
    }

关于java - 大数乘法算法并不总是有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54223367/

相关文章:

java - 内存泄漏在 MAT 中显示为 GC root : Native Stack

java - 从字符串中删除 �

algorithm - 在多个处理器上进行大整数模乘的最快运行时间是多少?

c++ - 处理大量

perl - 检查 Perl 模块中无穷大的最佳方法是什么?

java - 将大文件上传到 Amazon S3 时出现问题

java - wildfly 10 - javax.servlet.ServletContainerInitializer : Provider org. springframework.web.SpringServletContainerInitializer 不是子类型

SQL 行乘法。 select 中同一个表中的数据彼此相邻

c++ - 矩阵实现基准,我应该鞭策自己吗?

android - 大数的 SHA256 哈希结果在 Android 和 iOS 上不同