C++ 高精度整型数 - 采用位压缩策略优化

测试题目链接:https://www.luogu.com.cn/problem/P1932

本篇博客主要讲位压缩策略,不懂高精度思路的可以参考我上一篇博客:https://www.cnblogs.com/peichaoL/p/12516987.html

如果我们想存储一个高精度整型数:123456789

假如我们这么存:

 

 

 一个 int 拿着 4 字节的资源却只存了一位数字,这无疑是对内存的极大浪费,于是我们可以让一个 int 多存几位,像这样:

 

 

我们可以认为是将十进制变换成万进制,这便是位压缩的一个鲜明例子。

用这种方式来实现高精度整型数,最强大的地方在于对时间效率的优化:别的高精度一次加法出 1 位的结果,这种高精度一次加法出 4 位的结果。我们知道高精除法的实现中,假如我们正在求商的某一位(已做好对齐操作),试商的过程有两种:(下面所提及的除数 、被除数并不是真正的除数、被除数,而是计算到了某个过程时的除数、被除数,疑惑的童鞋可以参考我上一篇博客中对除法运算的详细解释。)

  • 用被除数一次次地减除数,直到小于除数。商的当前位即为减去的次数。
  • 二分去求这个位数,使得 该位数 * 除数 <= 被除数 且 (该位数 + 1) * 除数 > 被除数。 

假如我们采用传统的存储策略,即使二分地去找这个数,由于求的只是一位,所以只能在 1 ~ 9 之间二分,完全没有发挥二分的作用,对时间效率的提升极低。而如果我们用位压缩的策略去存储,就扩大了二分的范围,从而真正发挥其作用。(例如上图中,每一位所代表的权值 base = 10000,我们可以在 1 ~ 9999 之间二分,一次性求出商的 4 位)

对于位压缩中每位的权值 base:

base 的选取肯定是大越好,为了好实现,取10的整数次幂,对于 int[],考虑到乘法操作可能幂次翻倍,比较省事的取法是 base = 1e4,当然,如果细心点把可能溢出的地方用 long long 接住,可以取到 1e9。

附上类代码:

const int base = 10000;
const int digit = 4;
/**
 * @brief 高精度整型 - 采用位压缩策略
 * 要求无前导零 要求非负
 */ 
class HighPrecision{
private:
    int num[10000] = {0};
    int length = 1;
    int digital_num() const{
        HighPrecision temp = *this;
        int ans = (temp.length - 1) * digit;
        int temp2 = temp.num[temp.length - 1];
        while(temp2){
            ans++;
            temp2 /= 10;
        }
        return ans;
    }
public:
    HighPrecision(){}

    HighPrecision(const HighPrecision& a){
        for(int i = 0; i < a.length; i++)this->num[i] = a.num[i];
        this->length = a.length;
    }

    HighPrecision(const int& a){
        int temp = a;
        this->num[0] = temp % base;
        temp /= base;
        while(temp){
            this->num[this->length++] = temp % base;
            temp /= base;
        }
    }

    HighPrecision(const string& a){
        string s = a;
        while(s.length() > digit){
            this->num[this->length - 1] = atoi(s.substr(s.length() - 4).c_str());
            s.erase(s.length() - 4);
            this->length++;
        }
        this->num[this->length - 1] = atoi(s.c_str());
    }

/**
 * @brief 高精度加法
 * @param a 加数
 * @return 和
*/
    HighPrecision operator+ (const HighPrecision& a) const{
        HighPrecision ans;
        ans.length = max(this->length, a.length);
        int carry = 0;
        for(int i = 0; i < ans.length; i++){
            ans.num[i] = this->num[i] + a.num[i] + carry;
            carry = ans.num[i] / base;
            ans.num[i] %= base;
            if(i == ans.length - 1 && carry)ans.length++;
        }
        return ans;
    }

    HighPrecision operator+= (const HighPrecision& a){
        *this = *this + a;
        return *this;
    }    

/**
* @brief 高精度减法 - 必须保证被减数大于等于减数
* @param a 减数
* @return 差
*/
    HighPrecision operator- (const HighPrecision& a) const{
        HighPrecision ans = *this;
        int borrow = 0;
        for(int i = 0; i < this->length; i++){
            ans.num[i] -= a.num[i] + borrow;
            borrow = 0;
            if(ans.num[i] < 0){
                ans.num[i] += base;
                borrow = 1;
            }
        }
        while(!ans.num[ans.length - 1] && ans.length - 1)ans.length--;//去前导零
        return ans;
    }

    HighPrecision operator-= (const HighPrecision& a){
        *this = *this - a;
        return *this;
    }

/**
* @brief 高精度乘法
* @param a 乘数
* @return 积
*/    
    HighPrecision operator* (const HighPrecision& a) const{
        if(*this == 0 || a == 0)return HighPrecision(0);
        HighPrecision ans;
        int carry = 0;
        ans.length = this->length + a.length - 1;
        for(int i = 0; i < this->length; i++){
            carry = 0;
            for(int j = 0; j < a.length; j++){
                ans.num[i + j] += this->num[i] * a.num[j] + carry;
                carry = ans.num[i + j] / base;
                ans.num[i + j] %= base;
            }
            if(carry)ans.num[i + a.length] += carry;
        }
        if(carry)ans.length++;
        return ans;
    }

    HighPrecision operator*= (const HighPrecision& a){
        *this = *this * a;
        return *this;
    }

/**
* @brief 高精度除法, 请自觉检测除 0 的错误
* @param a 除数
* @return 商
*/
    HighPrecision operator/ (const HighPrecision& a) const{
        if(*this < a)return 0;
        HighPrecision ans, dividend = *this, divisor = a;
        while(divisor * HighPrecision(base) <= dividend)divisor *= HighPrecision(base);//初次对齐
        while(true){
            int high = base, low = 1, mid = 0;
            if(dividend >= divisor){
                while(high - low > 1){
                    mid = (high + low) / 2;
                    if(divisor * HighPrecision(mid) > dividend)high = mid;
                    else low = mid;
                }
                dividend -= divisor * HighPrecision(low);
                ans.num[ans.length - 1] += low;
            }
            if(divisor == a)break; 
            ans.length++;
            for(int i = 1; i < divisor.length; i++)divisor.num[i - 1] = divisor.num[i];
            divisor.num[divisor.length - 1] = 0;
            divisor.length--;
        }
        reverse(ans.num, ans.num + ans.length);
        while(!ans.num[ans.length - 1])ans.length--;
        return ans;
    }

    HighPrecision operator/= (const HighPrecision& a){
        *this = *this / a;
        return *this;
    }   

    HighPrecision operator% (const HighPrecision& a) const{
        return *this - (*this / a * a);
    }

    HighPrecision operator%= (const HighPrecision& a){
        *this = *this % a;
        return *this;
    }

    bool operator== (const HighPrecision& a) const{
        if(this->length != a.length)return false;
        for(int i = this->length - 1; i >= 0; i--){
            if(this->num[i] != a.num[i])return false;
        }
        return true;
    }

    bool operator> (const HighPrecision& a) const{
        if(this->length != a.length)return this->length > a.length;
        for(int i = this->length - 1; i >= 0; i--){
            if(this->num[i] != a.num[i])return this->num[i] > a.num[i];
        }
        return false;
    }

    bool operator< (const HighPrecision& a) const{
        return a > *this;
    }

    bool operator>= (const HighPrecision& a) const{
        return *this > a || *this == a;
    }

    bool operator<= (const HighPrecision& a) const{
        return *this < a || *this == a;
    }

    bool operator!= (const HighPrecision& a) const{
        return !(*this == a);
    }

    HighPrecision operator= (const HighPrecision& a){
        this->length = a.length;
        for(int i = 0; i < this->length; i++)this->num[i] = a.num[i];
        return *this;
    }

    friend ostream& operator<< (ostream&, const HighPrecision&);
};

ostream& operator <<(ostream& os, const HighPrecision& a){
    os << a.num[a.length - 1];
    for(int i = a.length - 2; i >= 0; i--){
        int temp_base = base / 10;
        while(a.num[i] < temp_base){
            cout << 0;
            temp_base /= 10;
        }
        if(a.num[i])cout << a.num[i];
    }
    return os;
}

 

posted @ 2020-03-18 22:25  sheeeeeeep  阅读(439)  评论(0编辑  收藏  举报