【问题标题】:Addition and subtraction of large numbers in Java [closed]Java中大数的加法和减法[关闭]
【发布时间】:2021-10-04 05:05:06
【问题描述】:

我有这个任务,我应该在其中添加或减去非常大的数字(超长)。我需要使用 String 和/或 StringBuilder 使其工作。

我这里有一些代码,但大多数情况下都不起作用,而且减法确实有问题。

 /** This class contains the method meant to calculate the sum of two large numbers. 
    */
public class SummingLargeNumbers {

  private SummingLargeNumbers() {}

  private static final char ZERO = '0';

  /**
   * Implements addition of two large numbers.
   *
   * @param s1 First number as string.
   * @param s2 Second number as string.
   * @return The answer as a string.
   */
  static String findSum(String s1, String s2) {
    if (s1 == null || s2 == null || s1.equals("") || s2.equals("")) {
      return "";
    }
    if (s1.length() > s2.length()) {
      String temp = s1;
      s1 = s2;
      s2 = temp;
    }
    StringBuilder result = new StringBuilder();
    int n1 = s1.length();
    int n2 = s2.length();
    int diff = n2 - n1;
    int denom = 0;
    for (int i = n1 - 1; i >= 0; i--) {
      int sum = ((int) (s1.charAt(i) - ZERO) + (int) (s2.charAt(i + diff) - ZERO) + denom);
      result.append((char) (sum % 10 + ZERO));
      denom = sum / 10;
    }

    for (int i = n2 - n1 - 1; i >= 0; i--) {
      int sum = ((int) (s2.charAt(i) - ZERO) + denom);
      result.append((char) (sum % 10 + ZERO));
      denom = sum / 10;
    }

    if (denom > 0) {
      result.append((char) (denom + ZERO));
    }

    return result.reverse().toString();
  }

  /**
   * Checks whether the first number is smaller than the second.
   *
   * @param s1 First number as a string.
   * @param s2 Second number as a string.
   * @return True if the first number is smaller than the second and false if otherwise.
   */
  static boolean isSmaller(String s1, String s2) {
    int firstLength = s1.length();
    int secondLength = s2.length();

    if (firstLength < secondLength) return true;
    if (secondLength < firstLength) return false;

    for (int i = 0; i < firstLength; i++) {
      if (s1.charAt(i) < s2.charAt(i)) return true;
      else if (s1.charAt(i) > s2.charAt(i)) return false;
    }
    return false;
  }

  /**
   * Implements subtraction of two large numbers.
   *
   * @param s1 First number as a string.
   * @param s2 Second number as a string.
   * @return The answer as a string.
   */
  static String findDiff(String s1, String s2) {
    if (s1 == null || s2 == null || s1.equals("") || s2.equals("")) {
      return "";
    }
    if (isSmaller(s1, s2)) {
      String temp = s1;
      s1 = s2;
      s2 = temp;
    }

    StringBuilder result = new StringBuilder();

    int n1 = s1.length();
    int n2 = s2.length();
    int diff = n1 - n2;

    int denom = 0;

    for (int i = n2 - 1; i >= 0; i--) {

      int sum =
          (((int) s1.charAt(i + diff) - (int) '0') - ((int) s2.charAt(i) - (int) '0') - denom);
      if (sum < 0) {
        sum = sum + 10;
        denom = 1;
      } else {
        denom = 0;
      }

      result.append(String.valueOf(sum));
    }

    for (int i = n1 - n2 - 1; i >= 0; i--) {
      if (s1.charAt(i) == '0' && denom > 0) {
        result.append("9");
        continue;
      }
      int sub = (((int) s1.charAt(i) - (int) '0') - denom);
      if (i > 0 || sub > 0) {
        result.append(String.valueOf(sub));
        denom = 0;
      }
    }
    if (result.reverse().indexOf("0") == 0) {
      result.deleteCharAt(0);
    }
    return result.toString();
  }
}

我真的不明白我哪里出错了,现在我还必须在一种方法中实现加法和减法,这对我来说真的很难。 如果我能对这整件事有所了解,我会很高兴,这样我就可以尝试让它发挥作用。

例如,一些不起作用的情况是:

-88131415555612 + 77020304444501 = 11111111111111

在这种情况下,它应该返回 -11111111111111

-1021315510 + 2183194 = 1019132316

这里也应该返回 -1019132316

与减法有关的大多数情况都是错误的。

【问题讨论】:

  • 我建议查看BigInteger 并使用该类或至少从中收集一些实现提示。至于“我哪里出错了” - 您应该使用调试器逐步检查您的代码并检查每一步的值。如果您了解您正在实施的算法,您应该能够发现错误。
  • @Thomas 我只能为此使用 String 或 StringBuilder。关于我使用学校逻辑的实现,我计算当前数字并进位,完成后我只需将剩余数字添加到较小的数字中。
  • 我只允许使用 String 或 StringBuilder,这就是问题所在。
  • 如果您提供一些不起作用的示例,可能会有所帮助。请注意,我们正在投入时间努力使其发挥作用。

标签: java string stringbuilder


【解决方案1】:

这看起来是一个有趣的问题,所以我试了一下。这是我最近一次测试的结果。

123 + 8900 = 9023
123 + 89 = 212
-123 + 893 = 770
-123 + -893 = -1016
123 + -893 = -770
999 + -999 = 0
999 + -997 = 2
-999 + 997 = -2
-88131415555612 + 77020304444501 = -11111111111111
-1021315510 + 2183194 = -1019132316
1021315510 + -2183194 = 1019132316
-1021315510 + -2183194 = -1023498704

我无法理解您的代码。你有太多的静态方法。如果您想知道,太多的静态字段和方法是一件需要避免的坏事。

那么,这就是你处理此类问题的方法。

将问题分解为越来越小的步骤,直到您可以对每个步骤进行编码。

这就是我的意思:

Sum two String numbers
    Return empty string if either String number is null or empty.
    Determine if the first String has a negative sign.
    Determine if the second string has a negative sign.
    If the signs are the same
        Add numbers.
    Else
        Subtract numbers.

Add numbers
    Determine if the first String has a negative sign.
    Strip signs from both numbers.
    Add numbers, starting from the right and going left.
    Append the carry, if any.
    Append the negative sign, if needed.

Subtract numbers
    Order the numbers by absolute value, largest first, then smallest.
    Determine if the largest String has a negative sign.
    Strip signs from both numbers.
    Subtract the smaller from the larger, starting from the right and going left.
    Append the negative sign, if needed.
    Remove leading zeros.

当然,在您编写完代码之后,这会容易得多。

从小处着手。我首先得到了相同的标志。然后我研究了相反的符号加法(减法)。

像文章一样组织您的代码。先放最抽象的代码,然后是更详细的代码。阅读您的代码的人永远不必回头查看顶部以了解某些东西是如何工作的。

这是完整的可运行代码。

public class SummingLargeNumbers {

    public static void main(String[] args) {
        SummingLargeNumbers sln = new SummingLargeNumbers();
        
        String s1 = "123";
        String s2 = "8900";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "123";
        s2 = "89";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "-123";
        s2 = "893";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "-123";
        s2 = "-893";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "123";
        s2 = "-893";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "999";
        s2 = "-999";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "999";
        s2 = "-997";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "-999";
        s2 = "997";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "-88131415555612";
        s2 = "77020304444501";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "-1021315510";
        s2 = "2183194";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "1021315510";
        s2 = "-2183194";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
        
        s1 = "-1021315510";
        s2 = "-2183194";
        System.out.println(s1 + " + " + s2 + " = " + sln.findSum(s1, s2));
    }
    
    public String findSum(String s1, String s2) {
        if (s1 == null || s2 == null || s1.isEmpty() || s2.isEmpty()) {
            return "";
        }
        
        boolean s1IsNegative = s1.charAt(0) == '-';
        boolean s2IsNegative = s2.charAt(0) == '-';
        
        if (s1IsNegative == s2IsNegative) {
            return addNumbers(s1, s2);
        } else {
            return subtractNumbers(s1, s2);
        }
    }
    
    private String addNumbers(String s1, String s2) {
        boolean s1IsNegative = s1.charAt(0) == '-';
        s1 = stripSign(s1);
        s2 = stripSign(s2);
        int index1 = s1.length() - 1;
        int index2 = s2.length() - 1;
        int carry = 0;
        
        StringBuilder builder = new StringBuilder();
        // Add the two String numbers together;
        while (index1 >= 0 || index2 >= 0) {
            int digit1 = (index1 >= 0) ? convertToInt(s1.charAt(index1--)) : 0;
            int digit2 = (index2 >= 0) ? convertToInt(s2.charAt(index2--)) : 0;
            int sum = digit1 + digit2 + carry;
            carry = sum / 10;
            int digit = sum % 10;
            builder.append(digit);
        }
        
        if (carry > 0) {
            builder.append(carry);
        }
        
        if (s1IsNegative) {
            builder.append('-');
        }
        
        return builder.reverse().toString();
    }
    
    private String subtractNumbers(String s1, String s2) {
        String[] order = orderNumbers(s1, s2);
        String larger = order[0];
        String smaller = order[1];
        boolean largerIsNegative = larger.charAt(0) == '-';
        larger = stripSign(larger);
        smaller = stripSign(smaller);
        int index1 = larger.length() - 1;
        int index2 = smaller.length() - 1;
        int borrow = 0;
        
        StringBuilder builder = new StringBuilder();
        // Subtract the smaller from the larger String number
        while (index1 >= 0 || index2 >= 0) {
            int digit1 = (index1 >= 0) ? convertToInt(larger.charAt(index1--)) : 0;
            int digit2 = (index2 >= 0) ? convertToInt(smaller.charAt(index2--)) : 0;
            int difference = digit1 - digit2 - borrow;
            if (difference < 0) {
                difference += 10;
                borrow = 1;
            } else {
                borrow = 0;
            }
            
            builder.append(difference);
        }
        
        if (largerIsNegative) {
            builder.append('-');
        }
        
        return removeLeadingZeros(builder);
    }
    
    /**
     * This method returns the String numbers in absolute value order.
     * 
     * @param s1 - String number 1
     * @param s2 - String number 2
     * @return - String array consisting of two values. The largest absolute value
     *         is first and the smallest absolute value is second.
     */
    private String[] orderNumbers(String s1, String s2) {
        boolean s1IsNegative = s1.charAt(0) == '-';
        boolean s2IsNegative = s2.charAt(0) == '-';
        s1 = stripSign(s1);
        s2 = stripSign(s2);
        String largest;
        String smallest;
        
        if (s1.length() < s2.length()) {
            largest = (s2IsNegative) ? "-" + s2 : s2;
            smallest = (s1IsNegative) ? "-" + s1 : s1;
        } else if (s1.length() > s2.length()) {
            largest = (s1IsNegative) ? "-" + s1 : s1;
            smallest = (s2IsNegative) ? "-" + s2 : s2;
        } else {
            boolean s1IsLarger = true;
            // Both Strings are the same size.  Find the largest digit,
            // starting from left to right;
            for (int index = 0; index < s1.length(); index++) {
                int digit1 = convertToInt(s1.charAt(index));
                int digit2 = convertToInt(s2.charAt(index));
                if (digit1 < digit2) {
                    s1IsLarger = false;
                    break;
                } else if (digit2 < digit1) {
                    s1IsLarger = true;
                    break;
                }
            }
            
            if (s1IsLarger) {
                largest = (s1IsNegative) ? "-" + s1 : s1;
                smallest = (s2IsNegative) ? "-" + s2 : s2;
            } else {
                largest = (s2IsNegative) ? "-" + s2 : s2;
                smallest = (s1IsNegative) ? "-" + s1 : s1;
            }
        }
        
        String[] output = new String[2];
        output[0] = largest;
        output[1] = smallest;
        
        return output;
    }
    
    private String removeLeadingZeros(StringBuilder builder) {
        int index = builder.length() - 1;
        boolean isNegative = builder.substring(index, index + 1).equals("-");
        
        if (isNegative) {
            builder.delete(index, index + 1);
            index--;
        }
        
        while (index > 0) {
            if (builder.substring(index, index + 1).equals("0")) {
                builder.delete(index, index + 1);
                index--;
            } else {
                break;
            }
        }
        
        if (isNegative) {
            builder.append('-');
        }
        
        return builder.reverse().toString();
    }
    
    private String stripSign(String s) {
        if (s.charAt(0) == '-' || s.charAt(0) == '+') {
            return s.substring(1);
        } else {
            return s;
        }
    }
    
    private int convertToInt(char c) {
        return (int) (c - '0');
    }

}

【讨论】:

  • 这个方案可行,但你不觉得有点太复杂了吗?
  • @Azhdan Yulmi:你会如何简化代码?
  • 这就是整个问题,我觉得整个事情都有一个更短的实现,但我似乎无法编写代码。您的解决方案运行良好,但认知复杂度非常高,我想知道是否有办法简化它。
  • @Azhdan Yulmi:好吧,如果您的代码不能解决问题,那么它的认知复杂度较低是否重要?我的代码的哪些部分具有非常高的认知复杂性?你的测量工具会按方法分解吗?
猜你喜欢
  • 2015-12-04
  • 2017-07-17
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
  • 2015-01-07
  • 2014-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多