四位压缩高精度

四位压缩高精度学习笔记

最近又学习了很多关于高精度的知识,也学习了C++的重载运算符等知识,这里根据高精度模板写下非负整数的四位压缩一些模板函数。


一、什么是四位压缩?

首先简单说一下高精度,就是int long等整数类型有上限,而double float等小数类型又会有精度丢失。高精度就是拓展内存来存更大范围的无精度丢失的数(大多记录整数),这个更详细的概念可以去查查。

那么我们怎么拓展内存呢?有个很常用的方法就是开一个数组,比如说int a[25],在每个单元存放 0 − 9 0-9 09的整数,它就可以存放 25 25 25位,这个范围已经超越long long类型,就可以满足一些需求了。

既然我们可以开数组来储存数字了,还加个这个四位压缩是个什么东东?别急,你看,一个int类型有 4 4 4字节 32 32 32位,可以存 2 31 − 1 2^{31}-1 2311大小的数,为 1 0 9 10^9 109数量级,你就用来存放 0 − 9 0-9 09​是不是有点浪费了呢,我们能不能有效利用这些空间呢?

这就是接下来所讲的四位压缩,就是在一个int单元里存放四位数字,即 0 − 9999 0-9999 09999(负数也可以存,这里先不考虑了)。为什么一定是四位,不能是五位吗?一个int的最大数量级为 1 0 9 10^9 109,你如果开五位的话,在乘法运算中,比如 99999 × 99999 99999\times99999 99999×99999,这一定是超限的,所以不能开五位,不然就会出错。

四位压缩有什么好处呢?我原先开的int a[25]本来只能存到 1 0 24 10^{24} 1024数量级,但我现在就能存到 1 0 99 10^{99} 1099数量级,内存利用效率上有了很大的提升;而且在同等数量级的运算下,比方说 1 0 24 10^{24} 1024,使用四位压缩的加法就只用for 6 6 6次,而原来的高精度就需要for 24 24 24次,说不定少一点这些时间就能AC了。


二、板子

在C++里建立一个类

const int maxlen = 105, maxmod = 10000;
class bigint{
private:
    //记录位数,这里是数组位数
    int len;
    //一个数组,放在动态内存里防止爆空间
    vector<int> num;
public:
    //默认构造函数
    bigint();
    //从long long读入
    bigint(long long x);
    //拷贝构造函数
    bigint(const bigint& other);
    //析构函数,做题时没啥用
    ~bigint();
    //重载赋值
    bigint& operator=(const bigint& other);
    //重载等于
    friend bool operator==(const bigint& a, const bigint& b);
    //重载大于
    friend bool operator>(const bigint& a, const bigint& b);
    //重载小于
    friend bool operator<(const bigint& a, const bigint& b);
    //重载加法
    friend bigint operator+(const bigint& a, const bigint& b);
    //重载加等
    friend bigint& operator+=(bigint& a, const bigint& b);
    //重载乘法
    friend bigint operator*(const bigint& a, const bigint& b);
    //重载乘等
    friend bigint& operator*=(bigint& a, const bigint& b);
    //重载输出流
    friend ostream& operator<<(ostream& out, const bigint& b);
};

下面我们一个个来看这些函数:

重载输出流

ostream& operator<<(ostream& out, const bigint& b) {
    out << b.num[b.len];//头部先出,没有多余的0
    for (int i = b.len - 1; i >= 1; i--) {
        if (b.num[i] == 0) {//如果是0,4位全是0
            out << "0000";
            continue;
        }
        for (int k = 10; k * b.num[i] < maxmod; k *= 10)//循环,少几个0就出几个
            out << '0';
        out << b.num[i];
    }
    return out;
}

其实这个输出是四位压缩的重头,四位压缩与普通高精度的区别主要在这里,就是得判断出几个0,其它的地方其实很多是将进位的10换成maxmod

默认构造函数

bigint::bigint() : len(1), num(maxlen, 0) {}

默认长度为1,否则没有输出;要开长度可以自行调整maxlen

这里试一下默认构造函数的输出:

int main() {
    bigint a;
    cout << a;
    return 0;
}

输出:

0

因为a中numlen都属于private,不能直接访问,可以将它们调到public里就能使用vector的成员函数了。

构造函数2

bigint::bigint(long long x) : num(maxlen, 0) {
    if (x == 0) {
        len = 1;
    }
    else {
        len = 0;
        while (x) {
            num[++len] = x % maxmod;
            x /= maxmod;
        }
    }
}

其实本来没有加x==0的判定的,在做题的时候用了赋值重载比如x=0,它自动就会设置长度为1,后面查出来是这的问题。

样例:

int main() {
    bigint a(123);
    cout << a;
    return 0;
}

输出:

123

拷贝构造函数

bigint::bigint(const bigint& other) : len(other.len), num(other.num) {}

样例:

int main() {
    bigint a(123);
    bigint b(a);
    cout << b;
    return 0;
}

输出:

123

重载赋值

bigint& bigint::operator=(const bigint& other) {
    if (this == &other) return *this;//如果是自己的话就不用重复操作了
    len = other.len;
    num = other.num;
    return *this;
}

有了重载赋值之后,就会有很有意思的事情了,比如

int main() {
    bigint a = 123;
    cout << a;
    return 0;
}

输出:

123

你可能会好奇,这个赋值怎么能通过呢?一面是bigint,另一面是int,怎么回事?这里有两个过程,一个是intlong long的转化,然后就是前面的构造函数1发挥作用了,这里临时生成了一个bigint的数据,然后赋值给了a。


重载等于

bool operator==(const bigint& a, const bigint& b) {
    if (a.len != b.len) return 0;
    for (int i = a.len; i > 0; --i) {
        if (a.num[i] != b.num[i]) return 0;
    }
    return 1;
}

样例:

int main() {
    bigint a = 123, b = 123;
    cout << (a == b);
    return 0;
}

这里有符号优先级问题需要注意一下.

输出:

1

重载大于

bool operator>(const bigint& a, const bigint& b) {
    if (a.len != b.len) return a.len > b.len;
    for (int i = a.len; i > 0; --i) {
        if (a.num[i] != b.num[i]) return a.num[i] > b.num[i];
    }
    return 0;
}

样例:

int main() {
    bigint a = 125, b = 123;
    cout << (a > b);
    return 0;
}

输出:

1

重载小于

bool operator<(const bigint& a, const bigint& b) {
    return b > a;
}

实际上调用大于就可以了,但是C++的面向对象函数比如要使用max(type,type)还是需要给出小于的定义的.

还有大于等于就是:!(a<b)

小于等于:!(a>b)


重载加法

bigint operator+(const bigint& a, const bigint& b) {
    bigint res;
    res.len = max(a.len, b.len);
    int carry = 0;
    for (int i = 1; i <= res.len; ++i) {
        res.num[i] = a.num[i] + b.num[i] + carry;
        carry = res.num[i] / maxmod;
        res.num[i] %= maxmod;
    }
    if (carry) res.num[++res.len] = carry;
    return res;
}

这里可以发现,比起正常的计算,就是将10替换成了maxmod

样例:

int main() {
    bigint a = 125, b = 123;
    cout << a + b;
    return 0;
}

输出:

248

重载加等

bigint& operator+=(bigint& a, const bigint& b) {
    a = a + b;
    return a;
}

重载乘法

bigint operator*(const bigint& a, const bigint& b) {
    bigint res;
    res.len = a.len + b.len - 1;//设置最高位
    for (int i = 1; i <= a.len; ++i) {//轮换着加乘
        for (int j = 1; j <= b.len; ++j) {
            res.num[i + j - 1] += a.num[i] * b.num[j];
        }
    }
    for (int i = 1; i <= res.len; ++i) {//考虑进位
        res.num[i + 1] += res.num[i] / maxmod;
        res.num[i] %= maxmod;
    }
    while (res.num[res.len + 1] > 0) {//进位
        ++res.len;
        res.num[res.len + 1] += res.num[res.len] / maxmod;
        res.num[res.len] %= maxmod;
    }
    while (res.len > 1 && res.num[res.len] == 0) --res.len;//超出的部分删去
    return res;
}

样例:

int main() {
    bigint a = 125, b = 123;
    cout << a * b;
    return 0;
}

输出:

15375

重载乘等

bigint& operator*=(bigint& a, const bigint& b) {
    a = a * b;
    return a;
}

这些就是相关的模板了。少了减法、负数、除法等,但是感觉现在做题还基本用不太到,这些部分就留给读者了!

最后的整个可复制模板:

const int maxlen = 105, maxmod = 10000;

class bigint{
private:
    int len;
    vector<int> num;
public:
    bigint();
    bigint(long long x);
    bigint(const bigint& other);
    ~bigint();
    bigint& operator=(const bigint& other);
    friend bool operator==(const bigint& a, const bigint& b);
    friend bool operator>(const bigint& a, const bigint& b);
    friend bool operator<(const bigint& a, const bigint& b);
    friend bigint operator+(const bigint& a, const bigint& b);
    friend bigint& operator+=(bigint& a, const bigint& b);
    friend bigint operator*(const bigint& a, const bigint& b);
    friend bigint& operator*=(bigint& a, const bigint& b);
    friend ostream& operator<<(ostream& out, const bigint& b);
};

bigint::bigint() : len(1), num(maxlen, 0) {}

bigint::~bigint() {}

bigint::bigint(long long x) : num(maxlen, 0) {
    if (x == 0) {
        len = 1;
    }
    else {
        len = 0;
        while (x) {
            num[++len] = x % maxmod;
            x /= maxmod;
        }
    }
}

bigint::bigint(const bigint& other) : len(other.len), num(other.num) {}

bigint& bigint::operator=(const bigint& other) {
    if (this == &other) return *this;
    len = other.len;
    num = other.num;
    return *this;
}

bool operator==(const bigint& a, const bigint& b) {
    if (a.len != b.len) return 0;
    for (int i = a.len; i > 0; --i) {
        if (a.num[i] != b.num[i]) return 0;
    }
    return 1;
}

bool operator>(const bigint& a, const bigint& b) {
    if (a.len != b.len) return a.len > b.len;
    for (int i = a.len; i > 0; --i) {
        if (a.num[i] != b.num[i]) return a.num[i] > b.num[i];
    }
    return 0;
}

bool operator<(const bigint& a, const bigint& b) {
    return b > a;
}

bigint operator+(const bigint& a, const bigint& b) {
    bigint res;
    res.len = max(a.len, b.len);
    int carry = 0;
    for (int i = 1; i <= res.len; ++i) {
        res.num[i] = a.num[i] + b.num[i] + carry;
        carry = res.num[i] / maxmod;
        res.num[i] %= maxmod;
    }
    if (carry) res.num[++res.len] = carry;
    return res;
}

bigint& operator+=(bigint& a, const bigint& b) {
    a = a + b;
    return a;
}

bigint operator*(const bigint& a, const bigint& b) {
    bigint res;
    res.len = a.len + b.len - 1;
    for (int i = 1; i <= a.len; ++i) {
        for (int j = 1; j <= b.len; ++j) {
            res.num[i + j - 1] += a.num[i] * b.num[j];
        }
    }
    for (int i = 1; i <= res.len; ++i) {
        res.num[i + 1] += res.num[i] / maxmod;
        res.num[i] %= maxmod;
    }
    while (res.num[res.len + 1] > 0) {
        ++res.len;
        res.num[res.len + 1] += res.num[res.len] / maxmod;
        res.num[res.len] %= maxmod;
    }
    while (res.len > 1 && res.num[res.len] == 0) --res.len;
    return res;
}

bigint& operator*=(bigint& a, const bigint& b) {
    a = a * b;
    return a;
}

ostream& operator<<(ostream& out, const bigint& b) {
    out << b.num[b.len];
    for (int i = b.len - 1; i >= 1; i--) {
        if (b.num[i] == 0) {
            out << "0000";
            continue;
        }
        for (int k = 10; k * b.num[i] < maxmod; k *= 10)
            out << '0';
        out << b.num[i];
    }
    return out;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值