Сложение и вычитание больших целых чисел без Math.BigInteger

Итак, мне дали задание по использованию стеков. Задача этого задания состоит в том, чтобы создать программу, которая может складывать и вычитать очень большие целые числа (предположительно бесконечного размера) без использования каких-либо библиотек или импорта (например, без BigInteger).

Вот как я подошел к дополнению:


public Stack<Integer> sum(Stack<Integer> leadingStack, Stack<Integer> secondStack) {
    int carry = 0;
    Stack<Integer> resultStack = new Stack<Integer>();
    while (leadingStack.isEmpty() == false && secondStack.isEmpty() == false) {
        int result = 0;
        int dig1 = leadingStack.pop();
        int dig2 = secondStack.pop();
        int resultDig = 0;

        result = dig1 + dig2 + carry;
        resultDig = result % 10;
        carry = result / 10;
        resultStack.push(resultDig);
    }
    if (carry > 0)
        resultStack.push(carry);
    return resultStack;
}

Этот метод работает с некоторыми целыми числами, но не работает с другими. Например, если я ввожу 500 и 450, я получаю 950, как и ожидалось. Однако, если я ввожу 500 и 45, я получаю 45.


И вот как я подошел к вычитанию (очень похожий подход):


    public Stack<Integer> sub(Stack<Integer> leadingStack, Stack<Integer> secondStack) {
    boolean borrow = false;
    Stack<Integer> resultStack = new Stack<Integer>();
    while (leadingStack.isEmpty() == false && secondStack.isEmpty() == false) {
        int dig1 = leadingStack.pop();
        int dig2 = secondStack.pop();
        if (borrow = true) {
            dig1 -= 1;
            borrow = false;
        }
        if (dig1 - dig2 < 0) {
            dig1 += 10;
            resultStack.push(dig1 - dig2);
            borrow = true;
        }
    }
    return resultStack;
}

Здесь очень похожая проблема. Например, если я вычту 50 и 45, я получу 4. Если я вычту 50 000 и 45 000, я получу 4 900.


Я уверен, что мне здесь не хватает чего-то простого, но я снова и снова просматривал код, и я не уверен, что это такое.


person Quinn    schedule 03.03.2017    source источник
comment
Сосредоточьтесь на одной части за раз. Используйте отладчик для пошагового выполнения кода и просмотра значений переменных на каждом этапе. Отладка — важный навык, и ему можно научиться только на практике.   -  person Code-Apprentice    schedule 03.03.2017
comment
Обратите особое внимание на то, как ваш метод add() обрабатывает числа с разным количеством цифр.   -  person Code-Apprentice    schedule 03.03.2017
comment
Разве вы не используете библиотеку, когда используете java.util.Stack?   -  person Andreas    schedule 03.03.2017
comment
Нет необходимости использовать == для сравнения логических значений, поскольку результатом является логическое значение, с которого вам уже нужно начинать. x == true это то же самое, что просто x. x == false совпадает с !x.   -  person Code-Apprentice    schedule 03.03.2017
comment
Также убедитесь, что вы знаете разницу между = и ==.   -  person Code-Apprentice    schedule 03.03.2017
comment
Какова цель if (borrow = true), или, возможно, вы имели в виду if (borrow == true), он же if (borrow)?   -  person Andreas    schedule 03.03.2017


Ответы (2)


В вашем коде есть несколько мест, которые требуют внимания:

  • Если стеки имеют разный размер, оставшаяся часть большего стека не обрабатывается.
  • При возврате стека результатов его элементы должны быть перевернуты, так как наименьшая цифра позиции помещается первой (и последняя позиция больше)
  • Смешанный оператор присваивания с равенством в if (borrow = true)
  • Метод вычитания не обрабатывает отрицательные числа

Все вместе в коде:

public Stack<Integer> sum(Stack<Integer> leadingStack, Stack<Integer> secondStack) {
  int carry = 0;
  Stack<Integer> resultStack = new Stack<Integer>();
  while (leadingStack.isEmpty() == false && secondStack.isEmpty() == false) {
    int dig1 = leadingStack.pop();
    int dig2 = secondStack.pop();
    int result = dig1 + dig2 + carry;
    int resultDig = result % 10;
    carry = result / 10;
    resultStack.push(resultDig);
  }

  Stack<Integer> leftStack = leadingStack.isEmpty() ? secondStack : leadingStack;
  while (leftStack.isEmpty() == false) {
    int dig = leftStack.pop();
    if (carry > 0) {
        dig += carry;
        carry = 0;
    }
    resultStack.push(dig);
  }

  if (carry > 0) resultStack.push(carry);
  return reverse(resultStack);
}

public Stack<Integer> sub(Stack<Integer> leadingStack, Stack<Integer> secondStack) {
  boolean borrow = false;
  Stack<Integer> resultStack = new Stack<Integer>();

  if (leadingStack.size() < secondStack.size()) {
    // Handle negative number
  }
  while (leadingStack.isEmpty() == false && secondStack.isEmpty() == false) {
    int dig1 = leadingStack.pop();
    int dig2 = secondStack.pop();
    if (borrow) {
        dig1 -= 1;
        borrow = false;
    }
    if (dig1 < dig2) {
        dig1 += 10;
        resultStack.push(dig1 - dig2);
        borrow = true;
    }
    else {
        resultStack.push(dig1 - dig2);
    }
  } 

  Stack<Integer> leftStack = leadingStack.isEmpty() ? secondStack : leadingStack;
  while (leftStack.isEmpty() == false) {
    int dig = leftStack.pop();
    if (borrow) {
      dig -= 1; 
      borrow = false;
    }
    resultStack.push(dig);
  }

  if (borrow) {
    // Handle negative number
  }
  return reverse(resultStack);
}

private Stack<Integer> reverse(Stack<Integer> inStack) {
  Stack<Integer> outStack = new Stack<>();
   while (inStack.isEmpty() == false) outStack.push(inStack.pop());
   return outStack;
}
person MaxZoom    schedule 03.03.2017
comment
Спасибо! все это были очевидные проблемы с моим кодом. Я реализовал часть кода leftStack, и все выглядит хорошо. Я не уверен, как я забыл обработать разный размер каждого стека. - person Quinn; 03.03.2017
comment
@Quinn Можете ли вы рассказать всем, как вы справлялись с отрицательными числами? - person MaxZoom; 10.03.2017

Я бы начал смотреть на это условие:

while (leadingStack.isEmpty() == false && secondStack.isEmpty() == false)

Это означает «пока оба стека не пусты». Но если у вас есть 2 числа с разным количеством цифр (скажем, 500 и 45), это не сработает (потому что один стек будет пуст, а другой нет, что приведет к выходу из цикла до обработки всех цифр). Поэтому вы должны изменить условие на «пока хотя бы один стек не пуст»:

while (leadingStack.isEmpty() == false || secondStack.isEmpty() == false)

Еще один совет: поскольку isEmpty() возвращает логическое значение, вам не нужно сравнивать с помощью ==, вы можете сделать:

while (! leadingStack.isEmpty() || ! secondStack.isEmpty())

Обратите внимание, что, поскольку один стек может быть пуст, а другой нет, вы должны проверить, пуст ли он перед вызовом pop:

int dig1 = 0;
if (!leadingStack.isEmpty()) {
    dig1 = leadingStack.pop();
}
int dig2 = 0;
if (!secondStack.isEmpty()) {
    dig2 = secondStack.pop();
}
person Community    schedule 03.03.2017
comment
Не сравнивайте логические значения с логическими литералами, чтобы получить логическое значение, потому что достаточно только логического значения. - person Lew Bloch; 03.03.2017
comment
@LewBloch Я также объясняю это в ответе - person ; 03.03.2017
comment
Да вы сделали. Извините за избыточность. - person Lew Bloch; 04.03.2017