1. 程式人生 > >資料結構-高精度大整數(二)

資料結構-高精度大整數(二)

接著上一篇文章的內容,這次來實現加減乘除演算法。

先來回顧一下上次學過的內容,給出大數類的儲存定義:

struct BigInteger
{
    friend BigInteger operator - (BigInteger, BigInteger);
    friend BigInteger operator + (BigInteger, BigInteger);

   // 表示大數的結構。數位按低位到高位的順序存放。儲存的是數位值,而不是數位的字元值。
    vector<int> digits;  // 存放數位值
    int signbit;         // 符號位。

    BigInteger();
    BigInteger(long long int);
    void zero_justify();
    BigInteger operator ^  (int);
    BigInteger operator << (int);
    BigInteger operator += (const BigInteger &);
    BigInteger operator -= (const BigInteger &);
    BigInteger operator *= (const BigInteger &);
    BigInteger operator /= (const BigInteger &);
};
typedef BigInteger BI;

加法運算。從低位往高位進行,每一位相加產生的進位向高位傳遞,注意負數的處理,可以相應的 轉變為減法來簡化操作。

BigInteger operator + (BigInteger a, BigInteger b)
{
    // 加法運算。從低位往高位進行,每一位相加產生的進位向高位傳遞,注意負數的處理,可以相應的
    // 轉變為減法來簡化操作。
    BigInteger c;
    int carry, i, t;  // 進位。
    // 兩數符號位相等,則最終符號位和原來的加數符號位相同。
    if(a.signbit == b.signbit)
        c.signbit = a.signbit;
    else
    {
        // 若兩個加數為一正一負,則將其轉換為減法操作。
        if(a.signbit == POSITIZE)
        {
            b.signbit = POSITIZE;
            c = a - b;
        }
        else
        {
            a.signbit = POSITIZE;
            c = b - a;
        }
        return c;
    }
     // 為兩數新增前導 0,以使得數位相同,便於計算。
    while(a.digits.size() < b.digits.size())
        a.digits.push_back(0);
    while(b.digits.size() < a.digits.size())
        b.digits.push_back(0);

     // 從動態陣列的開始即低位開始相加,逐漸往前進位,如果有的話。
    carry = 0;
    for(i = 0; i < a.digits.size(); i++)
    {
        t = a.digits[i] + b.digits[i] + carry; //逐位相加。
        c.digits.push_back(t % 10);
        carry = t / 10;
    }
    if(carry)  // 若最後一次進位為 1,則需在最高位加 1。
        c.digits.push_back(1);
    c.zero_justify();  //消除前導0

    return c;
}

過載加等於符.

BigInteger BigInteger::operator += (const BigInteger &b)
{
    //過載加等於符
    *this = *this + b;
     return *this;
}

減法。為了保證借位能夠成功終止,最好確保被減數不小於減數。同樣可以在適當情況轉變為加法來簡化操作。

BigInteger operator - (BigInteger a, BigInteger b)
{
    // 減法。為了保證借位能夠成功終止,最好確保被減數不小於減數。同樣可以在適當情況轉變為加法來簡化操作。
    BigInteger c;
    int borrow, i, t; // 借位。

    // 當某一個數為負數時,轉換為加法。
    if(a.signbit < 0 || b.signbit < 0)
    {
        b.signbit *= NEGATIVE;
        c = a + b;
        return c;
    }
    // 兩個都為正數,若 a < b,則儘量保證被減數大於減數。
    if(a < b)
    {
        c = b - a;
        c.signbit = NEGATIVE;
        return c;
    }
    c.signbit = POSITIZE;
    // 為減數新增前導 0,便於計算。因為先前已經使得 a > b 且 a 和 b 均為正數。
    while(b.digits.size() < a.digits.size())
        b.digits.push_back(0);

    borrow = 0;
    // 從低位開始逐位相減,不夠的向高位借位。
    for(i = 0; i < a.digits.size(); i++)
    {
        t = a.digits[i] - borrow - b.digits[i];

        if(t < 0)
        {
            t += 10;
            borrow = 1;
        }
        else
            borrow = 0;

        c.digits.push_back(t);
    }
    c.zero_justify();

    return c;
}

過載減等於符.

BigInteger BigInteger::operator -= (const BigInteger &b)
{
    //過載減等於符
    *this = *this - b;
     return *this;
}

乘法,採用一行一行算的方法比單純的反覆加法更快且編寫程式碼不會變複雜許多。每次操作都把第一個乘數左移一個數位,然後把左移後的數乘以x的積加到最終結果中,x是第二個乘數對應的數字,儘管反覆加法來實現這一步看起來不夠精巧,但由於第二個乘數的每個數字,內層迴圈最多執行9次,實際上並沒有浪費太多的時間,左移一個數位等價於用它乘以進位制的基數,在十進位制裡就是乘以 10。 

BigInteger operator * (const BigInteger &a,const BigInteger &b)
{
    int i, j;
    BigInteger c, row;

    row = a;
    row.signbit = a.signbit;
    for(i = 0; i < b.digits.size(); i++)
    {
        for(j = 0; j < b.digits[i]; j++)
            c += row;
        row = row << 1;
    }

    c.signbit = a.signbit * b.signbit;
    c.zero_justify();

    return c;
}

過載乘等於符.

BigInteger BigInteger::operator *= (const BigInteger &b)
{
    //過載乘等於符
    *this = *this * b;
     return *this;
}

除法運算。將餘數和商不斷左移,餘數加上被除數的下一個數字,然後嘗試減去除數。

BigInteger operator / (const BigInteger &a,const BigInteger &b)
{
    BigInteger c, row;
    int i, t;
    // 從數a的最高位開始除。
    for(i = a.digits.size() - 1; i >= 0; i--)
    {
        row = row << 1;
        row.digits[0] = a.digits[i];
         // 當除數小於被除數,減去除數,對應位的商增 1。
        t = 0;
        while(b <= row)
        {
            t++;
            row -= b;
        }
        // 將商左移 1 位,剛得到的商即為最低位。
        c = c << 1;
        c.digits[0] = t;
    }
    // 商的符號為 a 的符號乘以 b 的符號
    c.signbit = a.signbit * b.signbit;
    c.zero_justify();

    return c;
}

過載除等於符

BigInteger BigInteger::operator /= (const BigInteger &b)
{
    //過載除等於符
    *this = *this / b;
     return *this;
}

冪計算。冪就是反覆作乘法,利用:a^n = a^(n / 2) * a^(n / 2) * a^(n mod 2),可以減少乘法的次數。

BigInteger BigInteger::operator ^ (int n)
{
    if(n == 0)
        return BigInteger(1);
    if(n == 1)
        return *this;
    if(n == 2)
        return (*this) * (*this);

    return ((*this ^ (n/2)) ^ 2) * (*this ^ (n%2));
}