BigNum

BigNum problems involve math (usually arithmetic) involving number that cannot be stored in 64-bits, the largest commonly available data size.

A lot of programming languages offer BigNum library, such as Java (BigInteger/BigDecimal). There are several different BigNum libraries available on the net under GNU license, such as GNU MP Bignum library, but they are more practical to the development field rather than a competition setting.

This article is an attempt to demonstrate various techniques involved in BigNum computation, feasible to programming competitions. There are three main focuses of the code: Versatility, in which the same code could be applied to many different contest tasks Speed Optimization, in which algorithms are fast enough to handle the worst case scenario of 1000 digits accuracy and Code Optimization, in which the length of the code is kept at a reasonable level so it could be produced [bugfree] in a reasonable time frame.

A final note that the code samples uses of the C++ Standard Template Library, in an effort to take advantage of its various functionalities not present in C.

Preamble The code below uses some macros to keep the code short and tidy. Here they are:


 * 1) define pb push_back           //pushes an element into a vector
 * 2) define sz size              //size of a vector
 * 3) define fe first               //first element of a pair container
 * 4) define se second              //second element of a pair container
 * 5) define len length           //length of a string

typedef unsigned int uint;     //32-bit unsigned int typedef long long ll;          //64-bit signed long long typedef unsigned long long ull; //64-bit unsigned long long

BigInteger

Internal Structure There are two ways to interally represent BigInteger. The first way is what we generally use: a character array (or string) where each element/character represents a base 10 digit of the number. However, this turns out to be very inefficient when the numbers get large. The second way is to use a different radix (the base of the number system). Since the computer do integer computations in minimal time, it is preferable to use a very large radix so that this fact could be taken advantage of. Although in theory any radix would work, there are, in general, two reasonable choices: a power of 2, or a power of 10. The former is used because small tricks using binary numbers can be used to speed things up in most algorithms. However, there is a small trade off since almost always one would want to convert to and from the decimal system.

BigIntegers can be signed or unsigned.

typedef vector ubigint;        //unsigned BigInt typedef pair bigint;  //signed BigInt

So here, a radix of 2^32 is chosen for BigInt. A pair container is used for the signed version of BigInt. However, for the duration of the article, we will mostly discuss algorithms for the unsigned version. The signed versions are usually trivial extensions and so it will not be discussed in detail (for examples, check out the source code in the end of the tutorial).

Finally, it is worthwhile to note that the digits are stored such that the unit digit is in the beginning of the vector.

Addition/Subtraction

Multiplication

Division

String-Internal Format This occurs mostly when inputs are read. Notice that 10^9<2^32=4294967296. So to do this, we group every nine digits together and compute: $$((v_k2^{32}+v_{k-1})2^{32}+v_{k-2})2^{32}+...+v_0 $$ Using the multiplication (by integer) algorithm shown above, we achive a $$O(n^2)$$ algorithm.

Internal Format-String Almost always, it is necessary to output the resulting BigInt on to the screen, in decimal format. Hence a conversion is needed. We can extract the last 9 digit by computing $$v%10^9$$, using the division (by integer) method shown above. Dividing by $$10^9$$ and repeat the process yields the following $$O(n^2)$$ algorithm.

Source Code (Unfinished):
 * 1) include
 * 2) include
 * 3) include
 * 4) include


 * 1) define pb push_back
 * 2) define sz size
 * 3) define fe first
 * 4) define se second
 * 5) define len length

using namespace std;

typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef vector ubigint; typedef pair bigint;

ubigint add(ubigint &v1, ubigint &v2){ bool carry=false; int i; ull v;ubigint ret; for (i=0;i=(1ULL<<32); ret.pb((uint)v); } return ret; }

ubigint mul(ubigint &v1, uint v2, int shift=0){ int i; ull v; uint carry=0; ubigint ret(shift); for (i=0;i>32; ret.pb((uint)v); } if (carry>0) ret.pb(carry); return ret; }

ubigint mul(ubigint &v1, ubigint &v2){ ull carry=0; int i,j;ull v;ubigint ret(1),res; for (i=0;i div(ubigint &v1, uint v2){ int i; ull v=0; pair ret; for (i=0;i p; string ret; while(!iszero(v)){ p=div(v,1000000000); v=p.fe; ret=tostr(p.se)+ret; } return ret; }

bigint convert(string s){ bigint ret; if (s[0]=='-') {ret.fe=true;s=s.substr(1);} int i; ull v=0; i=s.length%9; for (i>0;i--){ v=v*10+s[i]; } ret.se.pb(v); s=s.substr(s.length%9); while(s.len>0){ v=0; for (i=0;i<9;i++) v=v*10+s[i]; s=s.substr(9); ret.se=mul(ret.se,1000000000); ret.se=add(ret.se,v); } return ret; }

int main{return 0;}