1. 程式人生 > 其它 >簡潔高效高精除法

簡潔高效高精除法

高精除法是高精裡面比較麻煩的。並且實現思路很多,這裡記錄一個模擬豎式計算的思路。

如何模擬十進位制的豎式除法?

除法需要一位一位從高到低得出答案,用被除數減去答案和除數的積,得到餘數作為下一輪被除數,繼續獲取下一位答案。

而獲取餘數的時候,我們將這一位的答案乘上除數,並於正在求解的這一位對齊後相減即可。

如何具體獲得每一位的答案呢?一位的範圍並不大,直接列舉即可。在壓位的情況裡(比如每位為1e9),可以在0到1e9之間二分答案。而這樣也是符合實際計算的。試想一下,手算100/13時,心裡想的也是“13x7=91小了,13x8過一百了,答案是7”,其本質就是二分。

另外,我們知道x位數乘以y位數只能是x+y

位數或者x+y-1位,所以x位數除以y位數,最多隻有x-y+1位數,從第x-y+1開始列舉即可。

int lans;

struct DecInt {
    int len, a[MAX];
    void trim() {
        while (len && !a[len-1]) --len;
    }
    DecInt operator*(int x) const {
        //高精乘低精
        DecInt ans = DecInt(len);
        long long num = 0;
        for (register int i = 0; i < len; i++) {
            num += (long long)a[i] * x;
            ans.a[i] = num % MOD;
            num /= MOD;
        }
        
        if (num) ans.a[ans.len++] = num;
        return ans.trim(), ans;
    }
    DecInt& operator-=(const DecInt &x) {
        //這裡的自減需要將被減數移動lans位後相減
        for (int i = 0; i < x.len; i++) {
            a[i + lans] -= x.a[i];
            if (a[i + lans] < 0)
                a[i + lans] += MOD, a[i + lans + 1]--;
        }
        for (int i = x.len + lans; i < len; i++)
            a[i] < 0 && (a[i] += MOD, a[i+1]--);
        return trim(), *this;
    }
    bool comp(const DecInt &t, const DecInt &divd) {
        //這裡的比較是比較答案和除數的積和被除數,需要將被除數移動ans位
        if (t.len != divd.len - lans) return t.len < divd.len - lans;
        for (int i = t.len-1; ~i; i--)
            if (t.a[i] != divd.a[i+lans]) return t.a[i] < divd.a[i+lans];
        return 1;
    }
    DecInt operator/(const DecInt &B) {
        lans = len - B.len;
        if (lans < 0)
            return DecInt("0", 1);
        DecInt ans = DecInt(lans + 1);

        while (~lans) {//從第lans位開始計算答案
            int l = 0, r = MOD-1;
            while (l < r) {
                //二分求解第lans位
                int mid = l + r + 1 >> 1;
                comp(B * mid, *this) ? l = mid : r = mid-1;
            }
            *this -= B * l;//將被除數減掉本位答案和除數的積
            ans.a[lans--] = l;
        }
        return ans.trim(), ans;
    }
};

可以看到,每次只取的被除數去掉後面lans位的數來進行操作(比如上例裡,第一次除時只比較"435",做減法時也是從"435"開始操作),提高的效率。

另外就是二分答案不能寫錯,乘積小了增大l,乘積大了減小r,而正好相等時,mid也有可能是答案,不能被排掉。