C++编写大整数类型BigInteger详细解析

题目

C++编写大整数类型BigInteger详细解析

叙述

大整数类在算法题中一般出现在精度上的问题,虽然Java上有现成的大整数类,而且还比较好用,但是我们也可以了解一下大整数类的实现,然后接下来的代码和算法入门竞赛第二版的实现是差不多的,但是我对整个算法做了一定的分析

代码


struct BigInteger
{
    static const int BASE = 100000000;
    static const int WIDTH = 8;
    vector<int> s;

    // 构造函数
    BigInteger(long long num = 0) 
    {
        // 调用 = 的重载符号
        *this = num;
    }

    // 重载 = 符号
    // 当输入在long long范围
    BigInteger operator = (long long num)
    {
        s.clear();
        do {
            // 每 8 位存储一次, 不足 8 位算一个
            // 从低位到高位的存储
            // 比如 123456789
            // 23456789 1   按照出栈顺序即可
            s.push_back(num % BASE);
            num /= BASE;
        } while (num > 0);
        return *this;
    }
    // 超过long long范围
    BigInteger operator = (const string& str)
    {
        s.clear();
        // len = 计算整个 str 有多少个八位数, 以及最后不住八位的算上一组
        int x, len = (str.length() - 1) / WIDTH + 1;
        for (int i = 0; i < len; ++i)
        {
            // 从字符串的后八位开始存储
            // 从低位到高位存储
            // 比如 123456789123456789
            // 23456789 34567891 12 按照出栈顺序即可
            int end = str.length() - i * WIDTH;
            int start = max(0, end - WIDTH);
            // 将str获得八位,然后转化为c类型字符串,然后使用sscanf转换为整数x, 最后存储到s中
            sscanf(str.substr(start, end - start).c_str(), "%d", &x);
            s.push_back(x);
        }
        return *this;
    }
    // 重载输出 << 符号
    friend ostream& operator << (ostream& out, const BigInteger& x)
    {
        // 先出去第一个高位,以免中间不足八位使用0补充
        // 比如 123
        // 如果放入循环后输出就是 00000123
        out << x.s.back();
        // 为啥是s.size()-2, 由于下标从0开始,并且前面输出了最后一个数字,所以剩下的位置为size()-2
        for (int i = x.s.size() - 2; i >= 0; i--)
        {
            // 用于转化的字符数组
            char buf[20];
            // 将s[i]中的数字转换为八个字符的数组,如果不足八位的使用0补全
            sprintf(buf, "%08d", x.s[i]);
            // 将buf中每个字符添加到输出流中
            for (int j = 0; j < strlen(buf); ++j) out << buf[j];
        }
        return out;
    }
    // 重载输入 << 符号
    friend istream& operator >> (istream& in, BigInteger& x)
    {
        string s;
        // 如果输入为空,返回in
        if (!(in >> s)) return in;
        // 如果不为空,就将赋值到s中给复制给x
        // 调用 BigInteger的 = 符号
        x = s;
        return in;
    }
    // 四则运算符号重载
    // 加法 +
    BigInteger operator + (const BigInteger& b) const
    {
        // 加法涉及到超出进位
        BigInteger c;
        c.s.clear();
        for (int i = 0, g = 0; ; ++i)
        {
            // 循环跳出的条件是 g == 0 表示没有任何的数可以存储或者相加的时候,即最长的向量遍历完后
            // i >= s.size()是当前BigInteger遍历完后
            // i >= b.s.size()是b的遍历完后
            // 三个条件都满足的时候,才能说都相加完全
            if (g == 0 && i >= s.size() && i >= b.s.size()) break;
            // x存储上次循环超过八位的数
            int x = g;
            // i小于s中元素个数或者小于b.s的元素个数是就将其中的值加入到x中存储
            if (i < s.size()) x += s[i];
            if (i < b.s.size()) x += b.s[i];
            // 处理当前“位”的数,求余数是获得前8位数字,放入新的BigIntger中
            c.s.push_back(x % BASE);
            // g是这次超过八位的数字
            g = x / BASE;
        }
        return c;
    }
    // 减法 - 
    BigInteger operator - (const BigInteger& b)
    {
        BigInteger c;
        c.s.clear();
        if (*this > b)
        {
            int i, g;
            for (i = 0, g = 0; ; ++i)
            {
                // 这里同加法的解释
                if (g == 0 && i >= b.s.size()) break;
                int x = g;
                // 这里是如果小于后者,就向高位借 1
                // 然后前者 加上基础 BASE
                if (s[i] < b.s[i])
                {
                    s[i + 1] -= 1;
                    s[i] = s[i] + BASE;
                }
                if (i < s.size()) x += s[i];
                if (i < b.s.size()) x -= b.s[i];
                c.s.push_back(x%BASE);
                g = x / BASE;
            }

            // 这个地方继续上面的将s的复制到c中
            for (int x = 0; i < s.size(); ++i)
            {
                x += s[i];
                c.s.push_back(x%BASE);
                x /= BASE;
            }
        }
        return c;
    }

    // 比较运算符的重载
    // 只需要一个小于重载后,就可以根据逻辑的推理,然后推出所有的符号
    // 小于 <
    bool operator < (const BigInteger& b) const
    {
        if (s.size() != b.s.size()) return s.size() < b.s.size();
        for (int i = s.size() - 1; i >= 0; --i)
        {
            if (s[i] != b.s[i]) return s[i] < b.s[i];
        }
        return false;
    }
    // 大于 >
    bool operator > (const BigInteger& b) const
    {
        return b < *this;
    }
    // 小于等于 <= 
    bool operator <= (const BigInteger& b) const
    {
        return !(b < *this);
    }
    // 大于等于 >=
    bool operator >= (const BigInteger& b) const
    {
        return !(*this < b);
    }
    // 不等于 !=
    bool operator != (const BigInteger& b) const
    {
        return b < *this || *this < b;
    }
    // 等于 ==
    bool operator == (const BigInteger& b) const
    {
        return !(b < *this || *this < b);
    }
};

总结

大部分的都总结了,但是乘法和除法以及其余的幂方算法没有添加,后面有机会能够实现,就继续分析,注释上几乎是对每个语句进行解释

参考资料

刘汝佳 算法竞赛入门经典第二版
c++大整数类的几种实现方法与解析

猜你喜欢

转载自blog.csdn.net/qq_36984327/article/details/80100476