Google Interview Question
SDE-2sCountry: India
Interview Type: Written Test
Good approach. I would also propose to use List<ulong> rather than List<byte> and Base = 10^19. This way you need only 64 bits to represent pointer in a list and 64 bits to represent 18-digits number = 128 bytes in total.
For List<byte> and Base = 10 you need: (64 bytes (to represent a pointer) + 1 byte to represent 1-digit number) * 18 = 65 * 18 = 1170 bytes to represent 18-digits number.
Approach is
1. Create a class "VeryLongINT"
2. it contains two fields 1. vector<int> 2. bool Sign (sign TRUE for +ve num, FALSE for -ve)
3. add operator methods to perform Addition and subtraction
4. whiel performing addition and subtraction operation we need to take care, "VeryLongINT" object sign.
5. E.g (obj1) + (-obj2), here I perform subtraction.
(obj1) + (-obj2) = obj1 - obj2
6. (-obj1) -(obj2) = -(obj+obj2);
// GoogleVeryLongInteger.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
class VeryLongINT
{
public:
VeryLongINT()
{
}
VeryLongINT(const vector<int> &vect, bool sign1 = true)
{
number = vect;
sign = sign1;
}
VeryLongINT(const VeryLongINT &obj)
{
number = obj.number;
sign = obj.sign;
}
void printNum()
{
cout << endl;
char ch = sign == true ? '+' : '-';
cout << ch;
for (int i = 0; i < number.size(); i++)
cout << number[i];
}
friend VeryLongINT operator+(VeryLongINT obj1, const VeryLongINT obj2);
friend VeryLongINT operator-(VeryLongINT obj1, const VeryLongINT obj2);
friend VeryLongINT operator-(VeryLongINT obj);
friend bool absCompare(const VeryLongINT &obj1, const VeryLongINT &obj2);
private:
vector<int> number;
bool sign; // sign flag TRUE if num is +ve otherwise false
};
// Add operator
VeryLongINT operator+(VeryLongINT obj1, VeryLongINT obj2)
{
if (obj1.sign == obj2.sign)
{
VeryLongINT result;
int i = obj1.number.size() - 1;
int j = obj2.number.size() - 1;
int carry = 0;
while (i >= 0 || j >= 0 || carry == 1)
{
int temp = 0;
if (i >= 0)
{
temp += obj1.number[i--];
}
if (j >= 0)
{
temp += obj2.number[j--];
}
temp += carry;
result.number.push_back(temp % 10);
carry = temp / 10;
}
reverse(result.number.begin(), result.number.end());
result.sign = obj1.sign;
return result;
}
else
{
// if obj1.sign is -ve
if (obj1.sign == false)
{
obj1.sign = true;
return obj2 - obj1;
}
// if obj2.sign is -ve
else
{
obj2.sign = true;
return obj1 - obj2;
}
}
}
//Subtract operator
VeryLongINT operator-(VeryLongINT obj1, VeryLongINT obj2)
{
if (obj1.sign == obj2.sign)
{
if (absCompare(obj1, obj2))
{
VeryLongINT result;
int i = obj1.number.size() - 1;
int j = obj2.number.size() - 1;
int carry = 0;
while (i >= 0 || j >= 0 || carry == 1)
{
if (j >= 0)
{
int temp = obj1.number[i] - carry - obj2.number[j];
carry = temp < 0;
if (carry)
{
temp += 10;
}
result.number.push_back(temp);
i--;
j--;
}
else
{
if (carry == 1)
{
int temp = -carry;
carry = 0;
temp += obj1.number[i];
if (temp > 0 || i > 0)
{
result.number.push_back(temp);
}
i--;
}
while (i >= 0)
{
result.number.push_back(obj1.number[i]);
i--;
}
}
}
reverse(result.number.begin(), result.number.end());
result.sign = obj1.sign;
return result;
}
else {
return -(obj2 - obj1);
}
}
else
{
return obj1 + (-obj2);
}
}
// unary operator
VeryLongINT operator-(VeryLongINT obj)
{
// just negate sign
obj.sign = obj.sign ? false : true;
return obj;
}
bool absCompare(const VeryLongINT &obj1, const VeryLongINT &obj2)
{
if (obj1.number.size() > obj2.number.size())
{
return true;
}
else if (obj1.number.size() < obj2.number.size())
{
return false;
}
else
{
for (int i = 0; i < obj1.number.size(); i++)
{
if (obj1.number[i] == obj2.number[i])
continue;
return obj1.number[i] > obj2.number[i] ? 1 : 0;
}
return true;
}
}
int main()
{
vector<int> a(3, 2);
vector<int> b(2, 1);
VeryLongINT *obj1 = new VeryLongINT(a,false);
VeryLongINT *obj2 = new VeryLongINT(b);
VeryLongINT result = *obj1 + *obj2;
obj1->printNum();
obj2->printNum();
cout << "\nAddition" << endl;
result.printNum();
free(obj1);
free(obj2);
cout << "\n*********" << endl;
vector<int> c(3, 2);
vector<int> d(4, 1);
VeryLongINT *obj3 = new VeryLongINT(c);
VeryLongINT *obj4 = new VeryLongINT(d);
result = *obj3 - *obj4;
obj3->printNum();
obj4->printNum();
cout << "\nSubtraction" << endl;
result.printNum();
free(obj3);
free(obj4);
cout << "\n*********" << endl;
getchar();
return 0;
}
I think, the main concern would be the following:
1. Verify the numbers the correct, since we don't want very long ints to be 1223567-789..
2. After add or sub considering the sign, there can be leading 0, one needs to remove those and have the sign appropriately.
3. Overflow or underflow will depend on the max length allowed. The max length also needs to be set while creating the number.
4. "0" value number should ideally have no sign, but keeping it + will help.
Also, in the above solution, it would be better to have vector<unsigned> instead of vector<int>.
Here is the C++ version of solution. Using vector<int> to store the numbers.
#include <vector>
#include <iostream>
using namespace std;
class BigInt{
public:
vector<int> numbers;
bool sign;
BigInt():sign(true)
{
sign = true;
}
BigInt(string value)
{
for (int i = 0 ; i < value.length(); i++ )
{
if (i == 0 && value[i] == '-')
sign = false;
else {
numbers.push_back('0'+value[i]);
}
}
}
BigInt(const BigInt& b)
{
this->sign = b.sign;
this->numbers = b.numbers;
}
BigInt& operator=(const BigInt& r)
{
sign = r.sign;
numbers = r.numbers;
return *this;
}
BigInt(vector<int> number, bool s)
{
numbers = number;
sign = s;
}
BigInt operator+ (BigInt& r)
{
BigInt result;
int lpos = (*this).numbers.size() -1;
int rpos = r.numbers.size() -1;
int carry = 0;
if (this->sign && r.sign)
result.sign = true;
else if (!this->sign && !r.sign)
result.sign = false;
else if (!this->sign)
return r - *this;
else
return (*this) - r;
while (rpos >= 0 || lpos >= 0)
{
int left = (lpos >= 0)? this->numbers[lpos] : 0;
int right = (rpos >= 0)? r.numbers[rpos] : 0;
int sum = left + right + carry;
carry = sum/10;
result.numbers.insert(result.numbers.begin(), sum%10);
lpos--;
rpos--;
}
if (carry > 0)
result.numbers.insert(result.numbers.begin(), carry);
return result;
}
BigInt operator- (BigInt& r)
{
BigInt result;
int carry = 0;
if (!this->sign && !r.sign)
{
r.sign = true;
return r - *this;
}else if (!this->sign && r.sign)
{
r.sign = false;
return (*this)+r;
}
else if (this->sign && !r.sign)
{
r.sign = true;
return (*this)+r;
}
BigInt* left = this;
BigInt* right = &r;
int c = compare(*this, r);
if (c == 0)
{
result.numbers.push_back(0);
return result;
} else if (c == -1)
{
left = &r;
result.sign = false;
right = this;
}
int lpos = left->numbers.size() -1;
int rpos = right->numbers.size() -1;
int diff = 0;
while (rpos >= 0 || lpos >= 0)
{
int lvalue = (lpos >= 0)? left->numbers[lpos] : 0;
int rvalue = (rpos >= 0)? right->numbers[rpos] : 0;
if (lvalue == 0 && lpos < 0)
{
diff = rvalue + carry;
if (result.sign) result.sign = false;
carry = 0;
}else if (lvalue < (rvalue + carry))
{
diff = 10+lvalue - rvalue - carry;
carry = 1;
} else {
diff = lvalue - rvalue - carry;
}
result.numbers.insert(result.numbers.begin() ,diff);
lpos--;
rpos--;
}
return result;
}
int compare(BigInt & l, BigInt & r)
{
if (l.numbers.size() == r.numbers.size())
{
for ( int i = 0; i < l.numbers.size(); i++)
{
if (l.numbers[i] != r.numbers[i])
return (l.numbers[i] > r.numbers[i])? 1 : -1;
}
}
else
return (l.numbers.size() > r.numbers.size())? 1 : -1;
return 0;
}
string to_string()
{
string result;
if (!this->sign)
result.push_back('-');
for (int i = 0; i < this->numbers.size(); i++)
result.push_back('0'+this->numbers[i]);
return result;
}
};
int main()
{
vector<int> int1;
int1.push_back(1);
int1.push_back(9);
int1.push_back(2);
int1.push_back(8);
BigInt bint1(int1, true);
vector<int> int2;
int2.push_back(2);
int2.push_back(0);
int2.push_back(0);
int2.push_back(0);
BigInt bint2(int2, true);
BigInt bint3;
bint3 = (bint1 + bint2);
cout << "result = " << bint3.to_string().c_str()<<endl;
BigInt bint4 = (bint2 - bint1);
cout << "result 2 = " << bint4.to_string().c_str()<<endl;
BigInt bint5 = (bint1 - bint2);
cout << "result 2 = " << bint5.to_string().c_str()<<endl;
vector<int> int6;
int6.push_back(5);
int6.push_back(2);
BigInt bint6(int6, true);
BigInt bint7 = bint6 - bint2;
cout <<" "<<bint6.to_string().c_str()<<" - " << bint2.to_string().c_str()<<" = " << bint7.to_string().c_str()<<endl;
BigInt answer = BigInt("675") - BigInt("899");
cout <<"675 - 899 =" <<answer.to_string().c_str() <<endl;
BigInt answer2 = BigInt("675") - BigInt("-899");
cout <<"675 - (-899) =" <<answer2.to_string().c_str() <<endl;
return 1;
}
My implementation uses a maximum of 104 integer to store the integer data
using System;
using System.Collections.Generic;
using System.Text;
class VBI : IComparable<VBI>
{
const ulong MAXDP1 = 0x100000000;
const uint MAXD = 0xFFFFFFFF;
const int MAXN = 104;
int sign = 0;
List<uint> data = new List<uint>();
public VBI()
{
}
public VBI(VBI v, int s)
{
this.sign = v.sign * s;
this.data.AddRange(v.data);
}
public int CompareTo(VBI x)
{
return CompareTo(x, true);
}
public int CompareTo(VBI x, bool considerSign)
{
if (x == null)
return 1;
int ret;
if (considerSign)
{
ret = this.sign.CompareTo(x.sign);
if (ret != 0)
return ret;
if (this.sign == 0)
return 0;
}
ret = this.data.Count.CompareTo(x.data.Count);
if (ret != 0)
return ret * x.sign;
for (int i = data.Count - 1; i >= 0; i--)
{
ret = this.data[i].CompareTo(x.data[i]);
if (ret != 0)
return ret * x.sign;
}
return 0;
}
public static VBI _add(VBI a, VBI b, int sign)
{
VBI c = new VBI();
if (a.data.Count < b.data.Count)
{
VBI t = a;
a = b;
b = t;
}
int na = a.data.Count;
int nb = b.data.Count;
bool carry = false;
for (int i = 0; i < nb; i++)
{
ulong dc = (ulong)a.data[i] + (ulong)b.data[i];
if (carry)
dc++;
if (dc > MAXD)
{
carry = true;
c.data.Add((uint)dc & MAXD);
}
else
{
carry = false;
c.data.Add((uint)dc);
}
}
for (int i = nb; i < na; i++)
{
if (carry)
{
ulong dc = (ulong)a.data[i] + (ulong)1;
if (dc > MAXD)
c.data.Add((uint)(dc & MAXD));
else
{
carry = false;
c.data.Add((uint)dc);
}
}
else
c.data.Add(a.data[i]);
}
if (carry)
{
if (c.data.Count == MAXN)
throw new Exception(sign == 1 ? "Overflow" : "Underflow");
c.data.Add(1);
}
c.sign = sign;
return c;
}
public static VBI _sub(VBI a, VBI b, int sign)
{
VBI c = new VBI();
switch (a.CompareTo(b, false))
{
case 0:
return c;
case -1:
VBI t = a;
a = b;
b = t;
sign = -sign;
break;
}
int na = a.data.Count;
int nb = b.data.Count;
bool carry = false;
int zs = -1;
for (int i = 0; i < nb; i++)
{
ulong dc;
ulong da = a.data[i];
ulong db = b.data[i];
if (carry) db++;
if (da >= db)
{
carry = false;
dc = da - db;
}
else
{
carry = true;
dc = (MAXDP1 + da) - db;
}
if (dc == 0)
{
if (zs == -1)
zs = i;
}
else
{
if (zs != -1)
{
for (int j = zs; j < i; j++)
c.data.Add(0);
zs = -1;
}
c.data.Add((uint)dc);
}
}
for (int i = nb; i < na; i++)
{
uint dc;
if (carry)
{
if (a.data[i] == 0)
dc = MAXD;
else
{
dc = a.data[i] - 1;
carry = false;
}
}
else
dc = a.data[i];
if (dc == 0)
{
if (zs == -1) zs = -1;
}
else
{
if (zs != -1)
{
for (int j = zs; j < i; j++)
c.data.Add(0);
zs = -1;
}
c.data.Add(dc);
}
}
c.sign = sign;
return c;
}
public VBI(uint val, int sign)
{
if (val == 0)
return;
this.sign = sign;
this.data.Add(val);
}
public static VBI operator +(VBI x, VBI y)
{
if (x.sign == 0)
return new VBI(y, 1);
if (y.sign == 0)
return new VBI(x, 1);
if (x.sign == y.sign)
return _add(x, y, x.sign);
return _sub(x, y, x.sign);
}
public static VBI operator -(VBI x, VBI y)
{
if (x.sign == 0)
return new VBI(y, -1);
if (y.sign == 0)
return new VBI(x, 1);
if (x.sign == y.sign)
return _sub(x, y, x.sign);
return _add(x, y, x.sign);
}
public static VBI parse(string s)
{
VBI r = new VBI();
if (string.IsNullOrEmpty(s))
return r;
int sign = 1;
if (s[0] == '-')
{
sign = -1;
s = s.Substring(1);
}
if (s.Length > 1000)
throw new Exception("String too long");
for (int i = 0; i < s.Length; i++)
{
int d = s[i] - '0';
if (d < 0 || d > 9)
throw new Exception("Invalid string");
if (r.sign != 0)
{
uint carry = 0;
for (int j = 0; j < r.data.Count; j++)
{
ulong p = (r.data[j]) * ((ulong)10) + ((ulong)carry);
r.data[j] = (uint)(p & MAXD);
carry = (uint)(p >> 32);
}
if (carry != 0)
r.data.Add(carry);
}
r = r + new VBI((uint)d, 1);
}
r.sign = r.data.Count == 0 ? 0 : sign;
return r;
}
public override string ToString()
{
if (sign == 0)
return "0";
StringBuilder ret = new StringBuilder();
VBI c = new VBI(this, 1);
while (c.data.Count > 0)
{
uint carry = 0;
int trailingZeroCount = 0;
bool trailingZeros = true;
for (int i = c.data.Count - 1; i >= 0; i--)
{
ulong d = (ulong)((ulong)carry << 32) + (ulong)c.data[i];
uint q = (uint)(d / 10);
c.data[i] = q;
if (q == 0)
{
if (trailingZeros)
trailingZeroCount++;
}
else
if (trailingZeros)
trailingZeros = false;
carry = (uint)(d % 10);
}
ret.Insert(0, carry);
for (int i = 0; i < trailingZeroCount; i++)
c.data.RemoveAt(c.data.Count - 1);
}
if (sign < 0)
ret.Insert(0, "-");
return ret.ToString();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(VBI.parse("99999999999999999999") + VBI.parse("1"));
Console.WriteLine(VBI.parse("100000") - VBI.parse("1"));
}
}
C#
complete class with Test() method testing '+' operator
- IC October 23, 2015