Google Answers Logo
View Question
 
Q: Division of arbitrarily large numbers ( Answered 5 out of 5 stars,   2 Comments )
Question  
Subject: Division of arbitrarily large numbers
Category: Computers > Algorithms
Asked by: amirehsans-ga
List Price: $5.00
Posted: 06 Feb 2003 23:41 PST
Expires: 08 Mar 2003 23:41 PST
Question ID: 158378
Hi.  I'm supposed to write a java program that divides an arbitrarily
large integer by another one, both input as strings.

I have decided to break each number into chunks of 10^18 digits
(because each int variable in java can handle up to the number 2^63-1
which has about 19 digits). Now, what would be an algorithm, using
these chunks of numbers, to divide two numbers?

We are assuming that both integers can be composed of several of such
chunks.
(this is the case I'm really interested in)

Let's say I'm dividing two integers, A and B. A has 40 digits and B
has 25 digits.  40 = 4 + 18 + 18 and 25 = 7 + 18, so A would have 3
chunks (starting from the least significant digit) and B would have 2
chunks.

*** Now how can I calculate A / B by only manipulating these chunks
given Java's capabilities and limitations to operate on int
primitives?

* I am not supposed to use any classes except Integer and String
classes.
* I know how to convert from string to int and cast!

Thanx for helping out!

Clarification of Question by amirehsans-ga on 06 Feb 2003 23:46 PST
I know there are tons of web-pages talking about division, but they
either get too technical and I don't inderstand their point, or
they're not geared towards my type of problem.  I would appreciate it
if you could put the solution in simple words (I know bases too, but
don't know division operation on numbers of base let's say 10^18).
Answer  
Subject: Re: Division of arbitrarily large numbers
Answered By: maniac-ga on 07 Feb 2003 05:53 PST
Rated:5 out of 5 stars
 
Hello Amirehsans,

The general name for arbitrary precision arithmetic is called
"bignum". Bignum arithmetic has been implemented in almost any
programming language - the FAQ at
  http://www.csc.fi/math_topics/Mail/FAQ/msg00015.html
describes a number of specific implementations for reference.

One of the more simple implementations was one produced by MIT several
years ago. A page that describes it and some work done to make it
compile cleanly with gcc is at
  http://aqualinux.chez.tiscali.fr/commun/bignum.htm
and the source code can be downloaded from
  http://aqualinux.chez.tiscali.fr/commun/my_bignum.tar.gz
I will use the source code at
  MY_BIGNUM/bn/bnDivide.c
and the documentation at
  MY_BIGNUM/doc/bn.pdf
as a reference to describe the algorithms required. The source code in
bnDivide.c is about three pages and has comments that explain each
step. There are some support functions also in
  MY_BIGNUM/KerN.c
as well - the "single digit" division is in this latter file (another
two-three pages long).

Note that the term "digit" in this code (and what I describe) refers
to a 32 bit integer.

The function to do a general division is BnnDivide; dividing N by D to
produce result Q and remainder R. The code starts with a few special
cases at the top (D/N = 0, N/1 = N, single "digit" division). Then it
does a normalization, calls an internal divide routine, and then
adjusts the results (based on the original normalization). This
normalization basically shifts the value left based on the value of
the divisor. For example, if you divide 100/10 in base 10 digits,
normalization would shift the divisor (10) left by one position,
divide 100/100 = 1, and shift that one position left to get the result
of 10. This method works for digits of any base.

The internal function divide has a nice block comment at the start
illustrating the layout of the numbers in memory. That layout is the
result of the normalization mentioned before. A few initialization
steps at the start - then a loop that implements long division, step
by step. You should be able to see how it works through the numbers -
dividing two digits by one at each step (BnnDivideDigit in KerN.c),
making adjustments to the remainder and quotient as needed.

The code I refer to can implement bignums with 16 bit or 32 bit
arithmetic. If you stay with 32 bit digits, you may be able to
simplify some of the steps with 64 bit / 32 bit division in Java; the
code in KerN.c appears to do this (first half of the if statement).
The algorithm should also work with 64 bit values - but Java may have
some subtle differences in arithmetic than C.

To help check the results, I suggest an "oracle" (to generate values
known to be correct). If you do a search on
  java bignum source code
you will bring up references such as
  http://tns-www.lcs.mit.edu/manuals/java-api-1.1beta2/api/java.lang.Bignum.html
which describes an implementation you can use to check your work.

In summary, the method described here does...
 - special case checks (BnnDivide)
 - normalize values (BnnDivide)
 - loop (divide)
  o single digit divide (BnnDivideDigit)
   * loop 
   * compute quotient / remainder
  o adjust values (divide)
 - adjust values (BnnDivide)
The result should be a handful of pages of Java code (5-12 pages) to
implement these functions.

If parts of this are not clear to you, don't hesitate to request a
clarification.

  --Maniac

Request for Answer Clarification by amirehsans-ga on 08 Feb 2003 10:32 PST
Hi and thanks for your answer! If you could tell me one thing, then
everything wil be clarified for me: What is the division algorithm for
two numbers in base 10^n?  I really couldn't understand much from the
code. If you could illustrate your answer with an example, I would
really appreciate it.

Clarification of Answer by maniac-ga on 09 Feb 2003 15:29 PST
Hello Amirehsans,

As the separate comment says, base 10 (or 10^n) division can be
considered in the way you learned long division. Generate each digit
of the result from either repeated subtraction or by some other
method.

I can't directly convert the example to base 10^n arithmetic, so let
me take another approach to understand the code that I referred to. I
built a version where the "digits" were 8 bits in size (zero to 256)
and have added several print statements to display intermediate
values. The sample I used was
  500678 / 1000 = 500, remainder 678
The values are displayed in hexidecimal (for intermediate values) or
decimal (loop indicies). I will comment on the results to help relate
them to the code.

BnnDivide: nn (4) = 0007A3C6, dd (2) = 03E8, entry
[for reference, nn == 500678, the digits are 00, 07, A3, C6; dd ==
500, 03, E8]
BnnDivide: nn (4) = 01E8F180, dd (2) = FA00, nshift = 6
[after normalization, both values shifted left six positions]
   divide: nn (3) = E8F180, dd (2) = FA00
[same values, now at entry of divide function]
   divide: BaseMinus1 = FF
   divide: DDigit = FA
   divide: dd (2) = 0600
[D was replaced by Base-D]
[Now in the loop of divide, the first pass - the second half of the if
statement was taken.]
BDivDigit: nn (2) = 01E8, d = FA, entry
[first two digits of nn - 01, E8 = 488; d - FA = 250; result should be
Q=1, rem=238 - EE]
BDivDigit: so(BNP)>so(BND)
[size of BigNumberProduct is more than the size of BigNumberDigit.
This is the then part of the if statement. As the comment mentions,
having smaller chunks make the arithmetic simpler.]
BDivDigit: nl=1, qq=B0, quad=1, before loop
BDivDigit: nl=0, qq=01, quad=ee, in loop
BDivDigit: quad=ee, return
   divide: QApp = 01, digits not equal
[note - return value from BnnDivDigit of Quotient=01, remainder=EE,
check]
   divide: nn+ni (3) = 01EEF1, dd (2) = 0600, QApp = 01, compute
remainder
[second time through the divide loop, second half of the if taken]
BDivDigit: nn (2) = EEF1, d = FA, entry
[first two digits of nn - EE, F1 = 61169; d - FA = 250; result should
be Q=244 - F4, rem=169 - A9]
BDivDigit: so(BNP)>so(BND)
BDivDigit: nl=1, qq=EE, quad=ee, before loop
BDivDigit: nl=0, qq=F4, quad=a9, in loop
BDivDigit: quad=a9, return
   divide: QApp = F4, digits not equal
[note - correct result again, Quotient is F4=244, remainder A9=169]
   divide: nn+ni (3) = F4A980, dd (2) = 0600, QApp = F4, compute
remainder
   divide: nn (4) = 01F4A980, dd (2) = FA00, returning
BnnDivide: nn (4) = 01F4A980, dd (2) = FA00
[just prior to the normalization step]
BnnDivide: nn (4) = 01F402A6, dd (2) = 03E8
[values in nn are 01, F4 = 500 (result); 02, A6 = 678; dd are 03, E8 =
1000]

Watching the example run, we did not run all the code. However, the
remaining code works in a similar manner. It might also be easier to
understand if the code did not overwrite some of the variables used -
that would certainly be something to consider as you write your
version. The other thing to remember is the methods that work in this
example for 2^8 (base 256) works in the same way for 2^16, 2^32, and
so on. I made no changes to the algorithms - just changed a few
constants and declarations in the header file (to use 8 bit signed /
unsigned characters as the "digits").

In putting together this example, I ended up using the "bz" package as
well to call the functions in the bn directory. The main program was
about a dozen lines of code (to convert two strings to "BigZ"'s, do
the division, and print the result). I am including the test program
for reference below.

#include <stdio.h>
#include "BigZ.h"

int main () {
  BigZ A, B, C;

  A = BzFromString("500678", 10);
  B = BzFromString("1000", 10);
  C = BzDiv(A, B);
  printf("500678/1000 is %s\n", BzToString(C, 10));
  return 0;
}

If there are still some unclear parts - let me know. Indicate
specifically which steps you are having trouble with so I can focus on
those steps.

  --Maniac
amirehsans-ga rated this answer:5 out of 5 stars
Greate answer! that's all I can say.

Comments  
Subject: Re: Division of arbitrarily large numbers
From: mathtalk-ga on 08 Feb 2003 20:46 PST
 
Hi, amirehsans-ga:

The simplest respectable algorithm for dividing multiprecision numbers
in base ten to a power (or any base for that matter) is essentially
the pencil and paper algorithm we learn in grade school, sometimes
called "long division".

One essentially uses the leading digits of the divisor and dividend to
"guess" a digit of the quotient.  There follows a multiplication of
the divisor by this "trial quotient" to see if the product is too
large to be subtracted (in suitable shifted fashion) from the
dividend.  If too large, the trial quotient (which is just a single
digit at a time) is reduced by one and so forth.  The trial quotient
is eventually found that works, and the subtraction of the product
from the dividend results in what we call a "partial remainder".  The
sequence of trial quotient digits yields a partial quotient.

The details vary a bit as to when one stops, depending on whether you
are trying to implement fixed point, floating point, or integer
arithmetic.  There are a couple of nice "tricks" that can improve the
efficiency of finding the right trial quotients, but it seems to me
you are more concerned with implementing something that works
correctly rather than the fastest possible code.

The simplest algorithm to code is called "division by repeated
subtraction".  It would actually agree with long division in base two
(since a trial quotient can only be 0 or 1), but becomes markedly more
inefficient when a larger base is involved.

Although you are thinking about using "chunks" of size 10^18 because
one of these will fit within a Java integer, I'd suggest using chunks
that are about the square root of that, e.g. 10^9, because you will
find it much more convenient if you can multiply two "digits" and
retain that simplest of products in a single variable.

regards, mathtalk
Subject: Re: Division of arbitrarily large numbers
From: amirehsans-ga on 09 Feb 2003 21:46 PST
 
Thanks very much to Maniac for his great answer and clarification and
to MathTalk for his insightful comment!!!

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy