[原创][C++]高精度运算算法(二):乘法与除法

3.3 乘法

高精度乘法运用竖式计算的思想,用一个数的某一位乘以另一个数的所有位。

例如:对于1234 * 56,我们用数组a和b分别存储1234和56,用数组c存储结果。

            a[3]        a[2]        a[1]        a[0]
            x                       b[1]        b[0]
________________________________________________________

            a[3]b[0]    a[2]b[0]    a[1]b[0]    a[0]b[0]
a[3]b[1]    a[2]b[1]    a[1]b[1]    a[0]b[1]
________________________________________________________
c[4]        c[3]        c[2]        c[1]        c[0]

通过这个我们可以找规律,假设a,b和c的下标分别为i,j和k,那么k=i+j。

综上,我们可以写出以下代码:

void multiply(string& charA, string& charB)
{
    int a[256] = {0}, b[256] = {0};
    init(charA, a);
    init(charB, b);
    int lenA = charA.length(), lenB = charB.length();
    
    int c[256] = {0};//c是结果
    int maxlen = lenA + lenB;
    
    for (int i = 0; i < lenA; i++)// 类似竖式计算
    {
        for (int j = 0; j < lenB; j++)
        {
            c[i + j] = a[i] * b[j];
            c[i + j + 1] += c[i + j] / 10;//进位
            c[i + j] %= 10;//进位
        }
    }
    //删除前导零
    while (c[maxlen - 1] == 0 && maxlen > 1) maxlen--;
    
    //输出
    for (int i = maxlen - 1; i >= 0; i--)
    {
        cout << c[i];
    }
    cout << endl;
}

3.4 除法

高精度除法也利用了竖式除法的思想。核心是逐位试商法。

  • ①从被除数的第一位开始,用此数除以除数,得出商和余数

  • ②用得出的余数与被除数的下一位数字结合继续除以除数,以此类推

3.4.1 高精度除以低精度

//一位数一位数地与除数相除,余数乘10加入下一位上
void div1(string charA, int x)//x是除数
{    
    int lenA = charA.size();
    int a[256] = {0};
    init(charA, a);

    int c[256] = {0};
    int remainder = 0;//余数

    for (int i = lenA - 1;i >= 0;i--)
    {
		remainder = remainder * 10 + a[i]; //模拟竖式除法中的落位 
		c[i] = remainder / x;
		remainder %= x;
    }
    
    //删除前导零
    while (c[lenA - 1] == 0 && lenA > 1) lenA--;
    
    //输出结果
    for (int i = lenA - 1; i >= 0; i--)
    {
        cout << c[i];
    }
    
    //如果余数不为0,输出余数
    if(remainder)
    {
        cout << "\nremainder: " << remainder << endl;
    }
}

3.4.2 高精度除以高精度

先定义一个函数isGreater,作用如下:

//被除数a的最低位下标为min_dg。lenB是除数b的长度,避免反复计算。
//此函数用于判断a最低位是否能再减去除数b而保持非负
inline bool isGreater(int a[], int b[], int min_dg, int lenB)
{
    //若被除数剩余部分比除数长,则返回true
    if (a[min_dg + lenB] != 0) return true;

    //从高位到低位,逐位比较
    for (int i = lenB - 1; i >= 0; i--)
    {
        if (a[min_dg + i] > b[i]) return true;
        if (a[min_dg + i] < b[i]) return false;
    }
    //相等的情况也返回true
    return true;
}

除法函数:

void div2(string charA, string charB)
{
    //c是商,d是被除数的剩余部分,算法结束之后即为余数
    int a[256] = {0}, b[256] = {0}, c[256] = {0}, d[256] = {0};
    int lenA = charA.length(), lenB = charB.length();
    init(charA, a);init(charB, b);

    //若除数为0,直接结束。
    if (lenB == 0)
    {
        cout << "除数不能为0!" << endl; 
        return;
    }

    //一开始,除数的剩余部分是它本身
    for (int i = 0; i < lenA; i++)
    {
        d[i] = a[i];
    }
    
    //模拟竖式长除法的过程:从下标lenA-lenB开始,从高位到低位计算商
    //如果觉得太抽象,可以手算一遍竖式除法加以理解
    for (int i = lenA - lenB; i >= 0; i--)
    {
        //判断第i位(也即min_dg)是否为最低位
        //也即是否可以再减去除数而保持非负
        while(isGreater(d, b, i, lenB))
        {
            //高精度减法
            for (int j = 0; j < lenB; j++)
            {
                d[i + j] -= b[j];
                if (d[i + j] < 0)
                {
                    d[i + j + 1] -= 1;
                    d[i + j] += 10;
                }
            }
            c[i] += 1;
        }
    }
    //删除前导零
    while (c[lenA - 1] == 0 && lenA > 1) lenA--;
    
    //输出结果
    for (int i = lenA - 1; i >= 0; i--)
    {
        cout << c[i];
    }

    //若有余数则输出余数
    if(d[0])
    {
        cout << "\nThe remainder is: " << d[0] << endl;
    }
}

4. 参考

高精度计算 - OI Wiki (oi-wiki.org)

1 个赞