1

I have a very large number represented by a string. Say String n = "64772890123784224827" . I want to divide the number by 3 efficiently. How can I do it? Some implementations are given below which can find out remainder. But how to get the quotient efficiently?

In Java, the number can be represented with BigInteger and the division operation can be done on BigInteger. But that takes too much time. Please help me find out the efficient way to divide this large number by 3.

Well following is a very basic implementation to find out the remainder:

#include <bits/stdc++.h>
using namespace std;

int divideByN(string, int);

int main()
{
    string str = "-64772890123784224827";
    //string str = "21";
    int N = 3;
    int remainder = divideByN(str, N);

    cout << "\nThe remainder = " << remainder << endl;

    return 0;
}

int divideByN(string s, int n)
{
    int carry = 0;
    int remainder = 0;

    for(int i = 0; i < s.size(); i++)
    {
        if(i == 0 && s.at(i) == '-')
        {
            cout << "-";
            continue;
        }

        //Check for any illegal characters here. If any, throw exception.

        int tmp = (s.at(i) - '0') + remainder * carry;
        cout << (tmp / n);

        if(tmp % n == 0)
        {
            carry = 0;
            remainder = 0;
        }
        else
        {
            remainder = tmp % n;
            carry = 10;
        }
    }

    return remainder;
}

Based on some good answers, here is a minimal implementation using lookup table to find out the remainder:

 #include <bits/stdc++.h>
using namespace std;

int divideByN_Lookup(string, int);

int lookup[] = { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0 }; //lookup considering 3 as divisor.

int main() {
    string str = "64772890123784224827";
    int N = 3;

    int remaninder_lookup = divideByN_Lookup(str, N);
    cout << "Look up implementation of remainder = " << remaninder_lookup
            << endl;

    return 0;
}

int divideByN_Lookup(string s, int n) {
    int rem = 0;
    int start = 0;

    if (s.at(start) == '-')
        start = 1;

    for (unsigned int i = start; i < s.size(); i++)
        rem = (rem + lookup[s.at(i) - '0']) % n;

    return rem;
}

What about quotient? I know I can process all characters one by one and add the quotient to a char array or string. But what is the most efficient way to find out the quotient?

coderx
  • 509
  • 6
  • 22
  • 2
    What's wrong with the paper&pencil method? – greybeard Jul 18 '15 at 18:51
  • Nothing wrong with that ... but in one coding competition, I found that that method also performs slow and time limit exceeds. So wondering if there is any more efficient method. – coderx Jul 18 '15 at 18:59
  • I fail to see how it could take any significant amount of time; it should definitely be faster than converting to "machine format", doing the computation, and re-converting to decimal. Care to share (the gist of) your code? – greybeard Jul 18 '15 at 19:15
  • Is your returned answer also supposed to be represented as a string of base-10 characters? – user2566092 Jul 18 '15 at 19:18
  • I need to return only the remainder after the division by 3. So the range of the remainder to be in the range [0, 2]. Retuning int is fine enough. – coderx Jul 18 '15 at 19:24
  • 1
    Think about modulus, then. (Now _that_ is a bad case of a question worded misleadingly - please make the question express you are looking for the remainder, not, as most would probably suppose, the quotient.) – greybeard Jul 18 '15 at 19:39
  • @greybeard - Please see the code and suggest optimization if any. – coderx Jul 18 '15 at 20:09
  • 2
    Change this question to reflect that you wanted something else than the quotient, which would be considered the result of a division more likely than not. If you still want answers on how to get the remainder mod 3 of a natural number represented as a string of decimal digits, ask that in a new question. Try to understand the comments (often containing hints to answers) and the answers. (Final hint here: to get that remainder fast, you _better not want_ to divide the whole number.) – greybeard Jul 18 '15 at 21:19
  • @greybeard - What's wrong in knowing how to find out the quotient efficiently? I wanted to know about remainder but now I want to find out the efficient way for quotient also. – coderx Jul 19 '15 at 08:02
  • You can put the test for '-' before the start of the loop to avoid doing it every time – samgak Jul 19 '15 at 08:05
  • 1
    `What's wrong in knowing how to find out the quotient efficiently?` - nothing. But actually determining the quotient takes time and doesn't (directly) contribute to the result required. – greybeard Jul 19 '15 at 08:18
  • @samgak - thanks! modified the code. – coderx Jul 19 '15 at 08:18
  • @greybeard - okay assume I want both the answers. Now how to make the time consuming quotient calculation optimized? – coderx Jul 19 '15 at 08:20
  • With C++-types: `std::div`. Else either your computation method yields both (like the paper&pencil method), or you compute the quotient and let remainder = dividend - quotient * divisor, given that multiplication (e.g. Toom-Cook) is probably faster than computing the remainder. (For non-small divisors. You are just doing an example of a small divisor. Other special cases include divisors equal to (powers of) the base used for representing the dividend, +/- small numbers.) – greybeard Jul 19 '15 at 08:28
  • Using `#include ` and `using namespace std;` are considered bad practice. For ``: https://stackoverflow.com/a/31816096/13647419 And `using namespace std;` kinda ruins the purpose of namespaces (ok, it may be ok in simple programs, but not in larger projects) – H-005 Oct 20 '20 at 18:24

4 Answers4

3

If all you need is the remainder after dividing by 3, make a look up table or function that converts each string character digit to an int, which is the remainder when you divide the digit by 3, and add up the ints across all digits in the string, and then there is a fact that the remainder when you divide your original number by 3 is the same as the remainder when you divide the sum of digits by 3. It would be virtually impossible to not be able to fit the sum of 0,1,2 values into a 32 or 64 byte integer. The input would simply have to be too large. And if it does start to become almost too large when you're summing the digits, then just take the remainder when you divide by 3 when you start getting close to the maximum value for an int. Then you can process any length number, using very few division remainder (modulus) operations (which is important because they are much slower than addition).

The reason why the sum-of-digits fact is true is that the remainder when you divide any power of 10 by 3 is always 1.

user2566092
  • 4,631
  • 15
  • 20
  • You might as well have the table return the digit mod 3, so 9 maps to 0, 8 maps to 2, 7 to 1, etc. Then when you add in a digit, if the sum exceeds 3 then subtract 3. Then there is no limit to the size of the number you can process. Though on actual processors it's probably faster to sum the digits and then do the mod at the end, so as to avoid branch prediction failures. – President James K. Polk Jul 18 '15 at 19:47
  • 1
    @JamesKPolk The subtract 3 probably would slow things down, but I agree since it's division by 3 instead of 9, we can simply convert each digit to either 0,1, or 2. And it might even be good to handle 2 or 3 digits at a time in the lookup table (only a few megabytes max memory usage if digits are 1 byte strings), although using a really small look up table for just one byte would probably be better for cache performance. Anyway, I'll update the answer to make the look up table map to 0,1,2. – user2566092 Jul 18 '15 at 19:59
  • @user2566092 - This idea is good enough and I implemented the idea with a minimal code. Code is attached to the question. Please suggest any more optimizations if possible. – coderx Jul 19 '15 at 08:09
1

I think you can start processing from the left, dividing each digit by 3, and adding the remainder to the next one. In your example you divide the 6, write 2, then divide the 4, write 1 and add the remainder of 1 to the 7 to get 17... Divide the 17... and so on.

EDIT:
I've just verified my solution works using this code. Note you may get a leading zero:

int main( int argc, char* argv[] )
{
  int x = 0;
  for( char* p = argv[1]; *p; p++ ) {
    int d = x*10 + *p-'0';
    printf("%d", d/3);
    x = d % 3;
  }
  printf("\n");
  return 0;
}

It's not optimal using so many divs and muls, but CS-wise it's O(N) ;-)

BitWhistler
  • 1,439
  • 8
  • 12
1

This is actually very simple. Since every power of 10 is equivalent to 1 modulo 3, all you have to do is add the digits together. The resulting number will have the same remainder when divided by 3 as the original large number.

For example:

3141592654 % 3 = 1

3+1+4+1+5+9+2+6+5+4 = 40

40 % 3 = 1
r3mainer
  • 23,981
  • 3
  • 51
  • 88
  • If the input string is very very vary large will I be able to store the sum of all digits in int / long / long long int field? – coderx Jul 18 '15 at 20:07
  • 1
    An unsigned 32-bit integer will be fine for any number of up to 477,218,588 digits. Otherwise use an unsigned 64-bit integer (OK up to 2 quintillion digits). – r3mainer Jul 18 '15 at 20:10
  • 1
    I'll just add that since `'0'` has an ASCII value of 48, you don't even need to convert the digits to integers first. Just add the byte values of the string together. (You'll have to use a 64-bit accumulator register though.) – r3mainer Jul 18 '15 at 20:19
0

I wrote this a while ago.. Doesn't seem slow :S

I've only included the necessary parts for "division"..

#include <string>
#include <cstring>
#include <algorithm>
#include <stdexcept>
#include <iostream>

class BigInteger
{
public:
    char sign;
    std::string digits;
    const std::size_t base = 10;
    short toDigit(std::size_t index) const {return index >= 0 && index < digits.size() ? digits[index] - '0' : 0;}

protected:
    void Normalise();
    BigInteger& divide(const BigInteger &Divisor, BigInteger* Remainder);

public:
    BigInteger();
    BigInteger(const std::string &value);

    inline bool isNegative() const {return sign == '-';}
    inline bool isPositive() const {return sign == '+';}
    inline bool isNeutral() const {return sign == '~';}

    inline std::string toString() const
    {
        std::string digits = this->digits;
        std::reverse(digits.begin(), digits.end());

        if (!isNeutral())
        {
            std::string sign;
            sign += this->sign;
            return sign + digits;
        }
        return digits;
    }

    bool operator < (const BigInteger &other) const;
    bool operator > (const BigInteger &other) const;
    bool operator <= (const BigInteger &other) const;
    bool operator >= (const BigInteger &other) const;
    bool operator == (const BigInteger &other) const;
    bool operator != (const BigInteger &other) const;

    BigInteger& operator /= (const BigInteger &other);
    BigInteger operator / (const BigInteger &other) const;
    BigInteger Remainder(const BigInteger &other) const;
};

BigInteger::BigInteger() : sign('~'), digits(1, '0') {}

BigInteger::BigInteger(const std::string &value) : sign('~'), digits(value)
{
    sign = digits.empty() ? '~' : digits[0] == '-' ? '-' : '+';
    if (digits[0] == '+' || digits[0] == '-') digits.erase(0, 1);

    std::reverse(digits.begin(), digits.end());
    Normalise();
    for (std::size_t I = 0; I < digits.size(); ++I)
    {
        if (!isdigit(digits[I]))
        {
            sign = '~';
            digits = "0";
            break;
        }
    }
}

void BigInteger::Normalise()
{
    for (int I = digits.size() - 1; I >= 0; --I)
    {
        if (digits[I] != '0') break;
        digits.erase(I, 1);
    }

    if (digits.empty())
    {
        digits = "0";
        sign = '~';
    }
}

bool BigInteger::operator < (const BigInteger &other) const
{
    if (isNeutral() || other.isNeutral())
    {
        return isNeutral() ? other.isPositive() : isNegative();
    }

    if (sign != other.sign)
    {
        return isNegative();
    }

    if (digits.size() != other.digits.size())
    {
        return (digits.size() < other.digits.size() && isPositive()) || (digits.size() > other.digits.size() && isNegative());
    }

    for (int I = digits.size() - 1; I >= 0; --I)
    {
        if (toDigit(I) < other.toDigit(I))
            return isPositive();

        if (toDigit(I) > other.toDigit(I))
            return isNegative();
    }
    return false;
}

bool BigInteger::operator > (const BigInteger &other) const
{
    if (isNeutral() || other.isNeutral())
    {
        return isNeutral() ? other.isNegative() : isPositive();
    }

    if ((sign != other.sign) && !(isNeutral() || other.isNeutral()))
    {
        return isPositive();
    }

    if (digits.size() != other.digits.size())
    {
        return (digits.size() > other.digits.size() && isPositive()) || (digits.size() < other.digits.size() && isNegative());
    }

    for (int I = digits.size() - 1; I >= 0; --I)
    {
        if (toDigit(I) > other.toDigit(I))
            return isPositive();

        if (toDigit(I) < other.toDigit(I))
            return isNegative();
    }
    return false;
}

bool BigInteger::operator <= (const BigInteger &other) const
{
    return (*this < other) || (*this == other);
}

bool BigInteger::operator >= (const BigInteger &other) const
{
    return (*this > other) || (*this == other);
}

bool BigInteger::operator == (const BigInteger &other) const
{
    if (sign != other.sign || digits.size() != other.digits.size())
        return false;

    for (int I = digits.size() - 1; I >= 0; --I)
    {
        if (toDigit(I) != other.toDigit(I))
            return false;
    }
    return true;
}

bool BigInteger::operator != (const BigInteger &other) const
{
    return !(*this == other);
}

BigInteger& BigInteger::divide(const BigInteger &Divisor, BigInteger* Remainder)
{
    if (Divisor.isNeutral())
    {
        throw std::overflow_error("Division By Zero Exception.");
    }

    char rem_sign = sign;
    bool neg_res = sign != Divisor.sign;
    if (!isNeutral()) sign = '+';

    if (*this < Divisor)
    {
        if (Remainder)
        {
            Remainder->sign = this->sign;
            Remainder->digits = this->digits;
        }
        sign = '~';
        digits = "0";
        return *this;
    }

    if (this == &Divisor)
    {
        if (Remainder)
        {
            Remainder->sign = this->sign;
            Remainder->digits = this->digits;
        }
        sign = '+';
        digits = "1";
        return *this;
    }

    BigInteger Dvd(*this);
    BigInteger Dvr(Divisor);
    BigInteger Quotient("0");

    Dvr.sign = '+';
    std::size_t len = std::max(Dvd.digits.size(), Dvr.digits.size());
    std::size_t diff = std::max(Dvd.digits.size(), Dvr.digits.size()) - std::min(Dvd.digits.size(), Dvr.digits.size());
    std::size_t offset = len - diff - 1;

    Dvd.digits.resize(len, '0');
    Dvr.digits.resize(len, '0');
    Quotient.digits.resize(len, '0');

    memmove(&Dvr.digits[diff], &Dvr.digits[0], len - diff);
    memset(&Dvr.digits[0], '0', diff);


    while(offset < len)
    {
        while (Dvd >= Dvr)
        {
            int borrow = 0, total = 0;
            for (std::size_t I = 0; I < len; ++I)
            {
                total = Dvd.toDigit(I) - Dvr.toDigit(I) - borrow;
                borrow = 0;

                if (total < 0)
                {
                    borrow = 1;
                    total += 10;
                }
                Dvd.digits[I] = total + '0';
            }
            Quotient.digits[len - offset - 1]++;
        }

        if (Remainder && offset == len - 1)
        {
            Remainder->digits = Dvd.digits;
            Remainder->sign = rem_sign;
            Remainder->Normalise();
            if (Remainder == this)
            {
                return *this;
            }
        }

        memmove(&Dvr.digits[0], &Dvr.digits[1], len - 1);
        memset(&Dvr.digits[len - 1], '0', 1);
        ++offset;
    }

    Quotient.sign = neg_res ? '-' : '+';
    Quotient.Normalise();

    this->sign = Quotient.sign;
    this->digits = Quotient.digits;
    return *this;
}

BigInteger& BigInteger::operator /= (const BigInteger &other)
{
    return divide(other, nullptr);
}

BigInteger BigInteger::operator / (const BigInteger &other) const
{
    return BigInteger(*this) /= other;
}

BigInteger BigInteger::Remainder(const BigInteger &other) const
{
    BigInteger remainder;
    BigInteger(*this).divide(other, &remainder);
    return remainder;
}

int main()
{
    BigInteger a{"-64772890123784224827"};
    BigInteger b{"3"};
    BigInteger result = a/b;
    std::cout<<result.toString();
}
Brandon
  • 22,723
  • 11
  • 93
  • 186