将字符串中表示的大数除以3

时间:2015-07-18 18:49:17

标签: c++ arrays algorithm math

我有一个由字符串表示的非常大的数字。 Say String n =" 64772890123784224827" 。我想有效地将​​数字除以3。我该怎么做?下面给出了一些可以找出余数的实现。但如何有效地获得商数?

在Java中,数字可以用BigInteger表示,除法操作可以在BigInteger上完成。但这需要太多时间。请帮我找出将这个大数字除以3的有效方法。

以下是一个非常基本的实现,以找出其余部分:

#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;
}

基于一些好的答案,这是一个使用查找表来查找余数的最小实现:

 #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;
}

商数怎么样?我知道我可以逐个处理所有字符并将商添加到char数组或字符串。但找出商的最有效方法是什么?

4 个答案:

答案 0 :(得分:3)

如果您只需要除以3之后的余数,请创建一个查找表或函数,将每个字符串数字转换为一个int,这是将数字除以3时的余数,并将整数加起来字符串中的所有数字,然后有一个事实是,当你将原始数字除以3时的余数与将数字之和除以3时的余数相同。实际上不可能不适合0,1,2值之和为32或64字节整数。输入只需要太大。如果在对数字求和时它确实开始变得几乎太大,那么当你开始接近int的最大值时,除以3除以余数。然后你可以使用非常少的除法余数(模数)运算来处理任何长度数(这很重要,因为它们比加法慢得多)。

数字和的事实为真的原因是当你将10的任何幂除以3时的余数总是1.

答案 1 :(得分:1)

我认为您可以从左侧开始处理,将每个数字除以3,然后将余数添加到下一个数字。 在你的例子中,你除了6,写2,然后除4,写1并将1的余数加到7得到17 ...除17 ...依此类推。

编辑:
我刚刚使用此代码验证了我的解决方案。请注意,您可能会得到一个前导零:

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;
}

使用这么多的div和muls并不是最优的,但CS-wise它是O(N); - )

答案 2 :(得分:1)

这实际上非常简单。由于每个10的幂等于1模3,所以你要做的就是将数字加在一起。当除以3作为原始大数时,得到的数字将具有相同的余数。

例如:

3141592654 % 3 = 1

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

40 % 3 = 1

答案 3 :(得分:0)

我刚才写过这篇文章..似乎并不慢:S

我只包括“师”的必要部分..

#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();
}