构建大数类

2014/11/19 01:08 am posted in  C++

白书你好

看书啊看书啊看书啊!

买了小大白书的kindle版,所以用稍微零碎的时间来看小白书,其实一直在说要看完白书啊看完白书啊,其实一批了木有看完。当然,小白对于这个阶段意义不是太大了,把语言篇直接pase,从算法篇开始,再过一遍。

其实看书是个很快的过程,理解用的时间也不是太多,更多的时间是花在总结上,我发现我自从建了博客写学习总结,一个学习过程有1/3是看书理解,1/3是琢磨写总结。几乎和再看一遍用的时间一样。记得开始的时候写数据结构总结,一章就是两三小时。

其实还是很纠结要不要再用这么多时间写总结的,那些不写总结的家伙,看书就是快。都无奈了。

大数问题

大数问题是进集训队第一周狂刷的东西,挺练模拟能力的,不过今天我才发现白书是用结构体和运算符重载把大数搞定的,我便模仿一下,不用结构体,直接用类,写个大数类,以后直接当模板。

大数,就是特别大的数,因为,大到longlong存储不下去,便需要用数组存储,因此,需要用数组来模拟加减乘除,这次我只打算搞加减乘。

构建大数类

存储:

要有长度,保存大数的长度,在计算,打印,都很有必要

要有负号标记,在处理的时候,知道这是一个大负数

一个长数组,用来保存大整数

class bign
{
private:
    bool sign;
    int len;
    int num[MAX_LEN];
}

 非计算操作:

要有初始化,赋值,包括字符串赋值,int赋值等,本来想和白书一样重载=运算符,但是突然发现我这是在写一个类啊,不是结构体,那么直接重载构造函数就可以了,所以写了无参数,字符串参数,整型参数的构造函数。

可以打印,直接友元函数重载

class bign
{
public:
    bign(){len = 0;memset(num,0,sizeof(num));}
    bign(char *s)
    {
        sign = 0;
        int s_len = strlen(s);
        memset(num,0,sizeof(num));

        if(s[0] == '-')
        {
            sign = 1;
            int i = 0;
            len = s_len - 1;
            for(char *p = s + s_len - 1;p > s;p--)
                num[i++] = *p - '0';
        }else
        {
            int i = 0;
            len = s_len;
            for(char *p = s + s_len - 1;p >= s;p--)
                num[i++] = *p - '0';
        }

    }
    bign(int n)
    {
        len = 0;
        sign = 0;
        memset(num,0,sizeof(num));
        if(n == 0)
        {
            len = 1;
        }else
        {
            if(n < 0)
            {
                sign = 1;
                n = -n;
            }
            while(n != 0)
            {
                num[len++] = n % 10;
                n /= 10;
            }
        }

    }

    friend ostream& operator << (ostream& os,bign n)
    {
        if(n.sign)
            os << '-';
        for(int i = n.len - 1;i >= 0;i--)
            os << n.num[i];
        return os;
    }
};

 大数计算

比较

在设计这些运算之前,我先搞定大数比较的问题,其实是想在大数减法上偷偷懒神马的。

//大数比较
    bool operator < (const Bigint & b) const
    {
        if(sign == b.sign)
        {
            if((!sign && !b.sign)&&len != b.len) return len < b.len;
            if((sign && b.sign)&&len != b.len) return len > b.len;
            if(sign)
            {
                for(int i = len - 1;i >= 0;i--)
                    if(num[i] != b.num[i]) return num[i] > b.num[i];
            }else
            {
                for(int i = len - 1;i >= 0;i--)
                    if(num[i] != b.num[i]) return num[i] < b.num[i];
            }
        }else
        {
            if(!sign && b.sign) return false;
            if(sign && !b.sign) return true;
        }
        return false;
    }

只需要写出一个小余,其他的操作都可以用小余来进行判断。

    bool operator > (const Bigint & b) const{return b < *this;}
    bool operator <= (const Bigint & b) const{return !(b < *this);}
    bool operator >= (const Bigint & b) const{return !(*this < b);}
    bool operator != (const Bigint & b) const{return b < *this || *this < b;}
    bool operator == (const Bigint & b) const{return !(b < *this) || !(*this < b);}

只为在减法的时候偷偷懒。

加法

在进行加法的时候进行了符号判断,虽然有点感觉是在画蛇添足,但是为了圆这个ADT式的谎,还是加上吧。

//加法
    bign operator + (const bign &b) const
    {
        bign c;
        //判断加后符号
        if(sign && b.sign)
            c.sign = 1;
        else if(!sign && b.sign)
            return c = b - *this;
        else if(sign && !b.sign)
            return c = *this - b;
        c.len = 0;
        for(int i = 0,g = 0;g || i < max(len,b.len);i++)
        {
            int x = g;
            if(i < len) x += num[i];
            if(i < b.len) x += b.num[i];
            c.num[c.len++] = x % 10;
            g = x / 10;
        }
        return c;
    }
    bign operator += (const bign &b)
    {
        *this = *this + b;
        return *this;
    }

 减法

有了大数比较之后,就可以轻松的偷懒了。

//减法
    Bigint operator - (const Bigint &b) const
    {
        Bigint c;
        c.len = 0;
        if(*this > b)
        {
            c.sign = 0;
            for(int i = 0;i < max(len,b.len);i++)
            {
                int x = 0;
                if(i < len) x += num[i];
                if(i < b.len) x -= b.num[i];
                if(c.len > 0 && c.num[c.len - 1] < 0){c.num[c.len - 1] += 10;x--;}
                c.num[c.len++] = x;
            }
        }else if(*this < b)
        {
            c.sign = 1;
            for(int i = 0;i < max(len,b.len);i++)
            {
                int x = 0;
                if(i < len) x -= num[i];
                if(i < b.len) x += b.num[i];
                if(c.len > 0 && c.num[c.len - 1] < 0){c.num[c.len - 1] += 10;x--;}
                c.num[c.len++] = x;
            }
        }else
        {
            c = 0;
        }

        while(c.num[c.len - 1] == 0 && c.len > 0)
            c.len--;
        if(c.len == 0) c.len = 1;

        return c;
    }
    Bigint operator -= (const Bigint &b)
    {
        *this = *this - b;
        return *this;
    }

 乘法

在写乘法之前特意去OJ找了两个加法和减法的题目,然后A掉,爽啊,想想还有最后一个乘法,常用大数类就搞出来了,那叫一个激动啊!!!

可以说乘法是比较难搞的,因为时间复杂度有点高。

//乘法
    Bigint operator * (const Bigint &b) const
    {
        Bigint c;
        if((sign && b.sign) || (!sign && !b.sign)) c.sign = 0;
        else c.sign = 1;

        //时间复杂度 O(len * b.len)
        for(int i = 0;i < len;i++)
            for(int j = 0;j < b.len;j++)
                c.num[c.len = max(i + j,c.len),i + j] += num[i] * b.num[j];

        for(int i = 0;i < c.len;i++)
        if(c.num[i] >= 10) {c.num[i + 1] += c.num[i] / 10;c.num[i] %= 10;}

        while(c.num[c.len] != 0)
            ++c.len;

        while(c.num[c.len - 1] == 0 && c.len > 0)
            c.len--;
        if(c.len == 0) c.len = 1;
        return c;
    }
    Bigint operator *= (const Bigint &b)
    {
        *this = *this * b;
        return *this;
    }

到此,常用大数类算是完成了。(除了除法)

大数类代码

#include <iostream>
#include <cstring>

using namespace std;

#define MAX_LEN 1500  //存储大整数的整型数组的长度

class Bigint
{
private:
    bool sign;//是否非负数
    int len;  //长度
    int num[MAX_LEN];
public:
    Bigint(){len = 0;memset(num,0,sizeof(num));}
    Bigint(char *s)
    {
        sign = 0;
        int s_len = strlen(s);
        memset(num,0,sizeof(num));

        if(s[0] == '-')
        {
            sign = 1;
            int i = 0;
            len = s_len - 1;
            for(char *p = s + s_len - 1;p > s;p--)
                num[i++] = *p - '0';
        }else
        {
            int i = 0;
            len = s_len;
            for(char *p = s + s_len - 1;p >= s;p--)
                num[i++] = *p - '0';
        }

    }
    Bigint(int n)
    {
        len = 0;
        sign = 0;
        memset(num,0,sizeof(num));
        if(n == 0)//特判
        {
            len = 1;
        }else
        {
            if(n < 0){sign = 1;n = -n;}
            while(n != 0){num[len++] = n % 10;n /= 10;}
        }

    }
    //打印大数
    friend ostream& operator << (ostream& os,Bigint &n)
    {
        if(n.sign)//判断符号
            os << '-';
        while(n.num[n.len - 1] == 0)
            n.len--;
        for(int i = n.len - 1;i >= 0;i--)
            os << n.num[i];
        return os;
    }
    //大数比较
    bool operator < (const Bigint & b) const
    {
        if(sign == b.sign)
        {
            if((!sign && !b.sign)&&len != b.len) return len < b.len;
            if((sign && b.sign)&&len != b.len) return len > b.len;
            if(sign)
            {
                for(int i = len - 1;i >= 0;i--)
                    if(num[i] != b.num[i]) return num[i] > b.num[i];
            }else
            {
                for(int i = len - 1;i >= 0;i--)
                    if(num[i] != b.num[i]) return num[i] < b.num[i];
            }
        }else
        {
            if(!sign && b.sign) return false;
            if(sign && !b.sign) return true;
        }
        return false;
    }
    bool operator > (const Bigint & b) const{return b < *this;}
    bool operator <= (const Bigint & b) const{return !(b < *this);}
    bool operator >= (const Bigint & b) const{return !(*this < b);}
    bool operator != (const Bigint & b) const{return b < *this || *this < b;}
    bool operator == (const Bigint & b) const{return !(b < *this) || !(*this < b);}
    //加法
    Bigint operator + (const Bigint &b) const
    {
        Bigint c;
        //判断加后符号
        if(sign && b.sign)
            c.sign = 1;
        else if(!sign && b.sign)
            return c = *this - b;
        else if(sign && !b.sign)
            return c = b - *this;
        c.len = 0;
        for(int i = 0,g = 0;g || i < max(len,b.len);i++)
        {
            int x = g;
            if(i < len) x += num[i];
            if(i < b.len) x += b.num[i];
            c.num[c.len++] = x % 10;
            g = x / 10;
        }
        return c;
    }
    Bigint operator += (const Bigint &b)
    {
        *this = *this + b;
        return *this;
    }
    //减法
    Bigint operator - (const Bigint &b) const
    {
        Bigint c;
        c.len = 0;
        if(*this > b)
        {
            c.sign = 0;
            for(int i = 0;i < max(len,b.len);i++)
            {
                int x = 0;
                if(i < len) x += num[i];
                if(i < b.len) x -= b.num[i];
                if(c.len > 0 && c.num[c.len - 1] < 0){c.num[c.len - 1] += 10;x--;}
                c.num[c.len++] = x;
            }
        }else if(*this < b)
        {
            c.sign = 1;
            for(int i = 0;i < max(len,b.len);i++)
            {
                int x = 0;
                if(i < len) x -= num[i];
                if(i < b.len) x += b.num[i];
                if(c.len > 0 && c.num[c.len - 1] < 0){c.num[c.len - 1] += 10;x--;}
                c.num[c.len++] = x;
            }
        }else
        {
            c = 0;
        }

        while(c.num[c.len - 1] == 0 && c.len > 0)
            c.len--;
        if(c.len == 0) c.len = 1;

        return c;
    }
    Bigint operator -= (const Bigint &b)
    {
        *this = *this - b;
        return *this;
    }
    //乘法
    Bigint operator * (const Bigint &b) const
    {
        Bigint c;
        if((sign && b.sign) || (!sign && !b.sign)) c.sign = 0;
        else c.sign = 1;

        //时间复杂度 O(len * b.len)
        for(int i = 0;i < len;i++)
            for(int j = 0;j < b.len;j++)
                c.num[c.len = max(i + j,c.len),i + j] += num[i] * b.num[j];

        for(int i = 0;i < c.len;i++)
        if(c.num[i] >= 10) {c.num[i + 1] += c.num[i] / 10;c.num[i] %= 10;}

        while(c.num[c.len] != 0)
            ++c.len;

        while(c.num[c.len - 1] == 0 && c.len > 0)
            c.len--;
        if(c.len == 0) c.len = 1;
        return c;
    }
    Bigint operator *= (const Bigint &b)
    {
        *this = *this * b;
        return *this;
    }
    //除法
    Bigint operator / (const long long b) const
    {
        Bigint c;
        c.sign = 0;
        long long n[MAX_LEN];
        long long n_len = 0;
        long long yushu = 0;

        int tf = 0;
        for(int i = len;i >= 0;i--)
        {
            if((yushu * 10 + num[i]) / b)
            {
                tf = 1;
                n[n_len++] = (yushu * 10 + num[i]) / b;
                yushu = (yushu * 10 + num[i]) % b;
            }else
            {
                if(tf)
                    n[n_len++] = 0;
                yushu = yushu * 10 + num[i];
            }
        }

        c.len = n_len;

        for(int i = 0;i < n_len;i++)
        {
            c.num[i] = n[n_len - 1 - i];
        }
        if(c.len == 0)
            c.len = 1;
        return c;
    }
    Bigint operator /= (const long long &b)
    {
        *this = *this / b;
        return *this;
    }
    //取余
    long long operator % (const long long b) const
    {
        long long yushu = 0;

        for(int i = len;i >= 0;i--)
        {
            if((yushu * 10 + num[i]) / b)
            {
                yushu = (yushu * 10 + num[i]) % b;
            }else
            {
                yushu = yushu * 10 + num[i];
            }
        }

        return yushu;
    }
    Bigint operator %= (const long long &b)
    {
        *this = *this % b;
        return *this;
    }
};