2

I'm creating a custom implementation of Java's BigInteger class, with subsequent methods:

  • multiply
  • divide,
  • add
  • subtract.

I have a problem with division.

private BigInt createQuotient (BigInt aNumber, BigInt other){
    BigInt dividend = new BigInt(aNumber.toString());
    BigInt divisor = new BigInt(other.toString());
    dividend.isNegative = false;
    divisor.isNegative = false;
    BigInt finalQoutient = new BigInt("0");
    BigInt one = new BigInt("1");

    while(compareBigInts(dividend, divisor) == 1) {
            BigInt two = one;
            BigInt lastTwo = new BigInt("0");
            BigInt temp = divisor;
            BigInt lastTemp = new BigInt("0");
        while(compareBigInts(dividend, temp) == 1) {
                lastTwo = two;
                lastTemp = temp;

                if (two == one) {
                        two = two.add(one);
                }
                else{
                        two = two.add(two);
                }
                temp = divisor.multiply(two);

        }

        finalQoutient = finalQoutient.add(lastTwo);
        dividend = dividend.subtract(lastTemp);


}
finalQoutient = finalQoutient.add(one);
return finalQoutient;
}

Code represents this algorithm:

Let's say 100 is our dividend and 5 is our divisor with 20 being our final quotient.

while dividend > divisor, do:

2^0 * 5 = 5 which is less than our dividend, so we increment two ;  
2^1 * 5 = 10 which is less than our dividend, so we increment two ;  
2^2 * 5 = 20 which is less than our dividend, so we increment two ;  
2^3 * 5 = 40 which is less than our dividend, so we increment two ;  
2^4 * 5 = 80 which is less than our dividend, so we increment two ; (lastTemp)  
2^4 * 5 = 160 which is greater than our dividend, so we save the value before this one ; (temp) 

We then take that saved value and remove it from the dividend, making it smaller. We simultaneously take that value and add it to a variable each time a loop is completed.

We do this until the dividend gets to be smaller than the divisor, at which point we simply return the variable that stored added lastTemp for each loop.

With that in mind, my program works for smaller numbers but becomes extremely slow as aNumber becomes larger. Since all my variables are shrinking I would expect each pass to be faster, however my program gets slower.

Why is this?

Full BigInt class.

https://github.com/collinarnett/big-int/blob/master/BigInt.java

  • Do you know what is [Big O notation](https://en.wikipedia.org/wiki/Big_O_notation)? You have n*n Algorithm! – Gatusko Jun 20 '18 at 21:40
  • 1
    The inner while loop is operating on a smaller number (since dividend shrinks) each time the outer while loop is run, therefore it should run faster with each pass of the outer while loop. @Gatusko – Collin Arnett Jun 20 '18 at 21:44
  • how does compare work? When the numerator is bigger, does it return 1? – fileyfood500 Jun 20 '18 at 22:33
  • @fileyfood500 Compare returns 1 when the numerator is greater than the denominator, -1 when the denominator is greater than the numerator and 0 when they are equal. – Collin Arnett Jun 20 '18 at 22:39
  • Looking at compare, it seems like the denominator must have fewer digits than the numerator (or the same), but maybe I'm misreading something or it's not relevant here – fileyfood500 Jun 20 '18 at 22:44
  • @fileyfood500 The code works, but becomes extremely slow once larger numbers are introduced. All numbers are also the same length since the method `addZerosIfNeeded` is called before the check. – Collin Arnett Jun 20 '18 at 22:46
  • You're finding the point where shifting the denominator to the left causes it to be greater than the numerator in a _very_ expensive way. Think about how you can determine the required shift without actually shifting and comparing the entire result. – Gene Jun 21 '18 at 01:55
  • @Gene This would still mean that my program would be the slowest right at the start, whereas the opposite is true is practice. – Collin Arnett Jun 21 '18 at 01:58
  • Did I understand you correctly that you'd expect that as `aNumber` grows, your program becomes faster? – yeputons Jun 21 '18 at 20:11
  • No, I am specifically referring to as `createQuoteint` processes `dividend` it should get faster, since `dividend` shrinks. @yeputons – Collin Arnett Jun 21 '18 at 21:02
  • @CollinArnett That makes sense, so what is your exact question/confusion? Each next pass is becoming slightly faster than the previous, but there are still a lot of passes for big numbers, so total running time grows significantly. – yeputons Jun 21 '18 at 21:30
  • @yeputons The program does not run faster after each pass in practice. – Collin Arnett Jun 22 '18 at 00:17
  • @CollinArnett how have you measured it? Please add measuring code, measuring steps and results to the question. – yeputons Jun 22 '18 at 09:19
  • Just by printing out any of the dynamic variables in the method you can see the calculations being done slower and slower. @yeputons – Collin Arnett Jun 22 '18 at 19:00
  • Btw, is `two == one` a bug? In Java `==` does not compare object's content at all (see [here](https://stackoverflow.com/questions/7520432/what-is-the-difference-between-vs-equals-in-java)). – yeputons Jun 22 '18 at 19:12

1 Answers1

2

Apparently, sizes of dividend.bigNum.size() and temp.bigNum.size() grow exponentially on each iteration. I've added

System.out.println(dividend + " " + temp + "; sizes are " + dividend.bigNum.size() + " " + temp.bigNum.size());

right before your innermost while loop and got the following:

366000000000 36; sizes are 12 12
56762354688 36; sizes are 24 24
18107649024 36; sizes are 48 48
8443972608 36; sizes are 96 96
3612134400 36; sizes are 192 192
1196215296 36; sizes are 384 384
592235520 36; sizes are 768 768
290245632 36; sizes are 1536 1536
139250688 36; sizes are 3072 3072

That happens in part because your BigInt(String) does not truncate heading zeros (probably should be done in createArrayList) and in part because they're doubled by createProduct in both arguments.

My recommendation for the future is to debug this issue the same way as if it were a correctness issue: print variable sizes, look at expected sizes, look at real values. Also, you can use profiler.

yeputons
  • 8,478
  • 34
  • 67
  • Yes this fixed my issue. I've added a method `removeZeros` that I've put at the end of these methods. Thank you so much. – Collin Arnett Jun 23 '18 at 18:49