高精度乘法and高精度除法

高精度乘法
讲到高精度乘法,其实大致和高精度加法相同,不同的是,高精度加法只是对两个数相加,但是高精度减法需要对多个数相加,原因是需要将一个数的个位到最高位分别与另一个数的个位到最高位相乘,再将这些数相加。

下面先看高精度乘法的核心代码。

for (int i = 0; i < len1; i ++){
    for (int j = 0; j < len2; j ++){
        c[j+i] += a[i] * b[j];
        c[j+1+i] += c[j+i] / 10;
        c[j+i] %= 10;
    }
}


不难发现,这个核心代码和高精度加法的区别就是:

高精度加法是c[i]+=a[i]+b[i];

但是高精度乘法是c[j+i] += a[i] * b[j];

这是因为 i 和 j 分别代表的是这两个数的位,我们知道当个位的1与十位的2相乘得到的是20,只需将2放在i+j位上就可以了。

由于i和j是从0开始的,所以就有i位的数与j位的数相乘后的数会放在i+j位上,这就是核心代码关键难理解的。

讲完核心代码,后面去除前导0的操作和输出也就很好看懂了。

while (c[len3-1] == 0 && len3 > 1)
    len3--;
for (int i = 0; i < len3; i ++)
    cout << c[len3-1-i];


下面用一道洛谷题目练习一下。

P1303 A*B Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

AC代码

#include<iostream>
#include<string>
using namespace std;
const int N = 100010;
int a[N], b[N], c[N];
string s1,s2;
int main()
{
    cin >> s1 >> s2;
    int len1=s1.size(),len2=s2.size();
    for (int i = 0; i < len1; i ++)
        a[len1-1-i] = s1[i] - '0';//字符串转换为数组 
    for (int i = 0; i < len2; i ++)
        b[len2-1-i] = s2[i] - '0';
    for (int i = 0; i < len1; i ++){
        for (int j = 0; j < len2; j ++){
            c[j+i] += a[i] * b[j];//位乘 
            c[j+1+i] += c[j+i] / 10;//进位 
            c[j+i] %= 10;
        }
    }
    int len3 = len1 +len2;
    while (c[len3-1] == 0 && len3 > 1)//去除前导0 
        len3--;
    for (int i = 0; i < len3; i ++)
        cout << c[len3-1-i];
    return 0;
}

但是我们可以很容易发现,这道题如果是用负数相乘的话就不能正确解决问题了、

由于这道题限制数是非负数,所以代码可以解决问题。

我们毕竟是想要自己的代码更加完善的,所以我们只需加一些优化即可。

#include<iostream>
#include<string>
using namespace std;
const int N = 100010;
int a[N], b[N], c[N];
string s1,s2;
int main()
{
    cin >> s1 >> s2;
    int len1=s1.size(),len2=s2.size();
    if(s1[0]=='-'&&s2[0]!='-'||s1[0]!='-'&&s2[0]=='-') cout<<'-';//找字符串的第一个元素,判断答案出现"-"的情况 
    if(s1[0]=='-')//判断完后,去除"-" 
    {
        for(int i=0;i<len1-1;i++){
            s1[i]=s1[i+1];//整体前移 
        }
        len1--;//由于失去"-",字符串长度减1 
    }
    if(s2[0]=='-')//同理 
    {
        for(int i=0;i<len2-1;i++){
            s2[i]=s2[i+1]; 
        }
        len2--;
    }
    for (int i = 0; i < len1; i ++)
        a[len1-1-i] = s1[i] - '0';//字符串用数组接受 
    for (int i = 0; i < len2; i ++)
        b[len2-1-i] = s2[i] - '0';
    for (int i = 0; i < len1; i ++){
        for (int j = 0; j < len2; j ++){
            c[j+i] += a[i] * b[j]; //位乘 
            c[j+1+i] += c[j+i] / 10; //进位 
            c[j+i] %= 10;
        }
    }
    int len3 = len1 +len2;
    while (c[len3-1] == 0 && len3 > 1) //去除前导0 
        len3--;
    for (int i = 0; i < len3; i ++)
        cout << c[len3-1-i];
    return 0;
}

高精度除法
高精度除法相比于前面的是最难的,高精度除法分为高精度除以低精度和高精度除以高精度,前者比较简单,但是后者还是比较难的。

1.高精度除以低精度
在这里只需要将一个数字当字符串输入,另一个用long long输入。

这里先来看一下核心代码。

for(int i=len1;i>=1;i--){
    remainder=remainder*10+a[i];
    ans[i]=remainder/b;
    remainder%=b;
}


这里的remainder是余数,当一开始时余数为0。

由上图示例,对24567这个数,先用2和除数36试除,得到商为0,用ans数组存下,再用24,商也是0,再用245除以36时,商为6,依次进行,核心代码就是起到这么一个作用。

在后面将多余的0去除就OK了。

下面出示例题。

P1480 A/B Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

AC代码。

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
string s1;
long long a[N],b,ans[N];
int main()
{
    cin>>s1>>b;
    int len1=s1.size();
    for(int i=0;i<len1;i++) a[len1-i]=s1[i]-'0';//转为数组 
    long long remainder=0;//初始余数为0 
    for(int i=len1;i>=1;i--){
        remainder=remainder*10+a[i];//模拟计算 
        ans[i]=remainder/b;
        remainder%=b;
    }
    while(ans[len1]==0&&len1>1) len1--;//去除前导0 
    for(int i=0;i<len1;i++){    
        cout<<ans[len1-i];
    }
    return 0;
}

老规矩,优化一下代码,让它的输入范围增大,可以输入负数。

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
string s1;
long long a[N],b,ans[N];
int main()
{
    cin>>s1>>b;
    if(b==0) 
    {cout<<"除数不能为0";return 0;}
    int len1=s1.size();
    if(s1[0]=='-'&&b>0||b<0&&s1[0]!='-') cout<<'-';//优化部分,同上 
 
    if(s1[0]=='-')
    {
        for(int i=0;i<len1-1;i++){
            s1[i]=s1[i+1];
        }
        len1--;
    }
    b=abs(b);
    for(int i=0;i<len1;i++) a[len1-i]=s1[i]-'0';//转为数组 
    long long remainder=0;//初始余数为0 
    for(int i=len1;i>=1;i--){
        remainder=remainder*10+a[i];//模拟计算 
        ans[i]=remainder/b;
        remainder%=b;
    }
    while(ans[len1]==0&&len1>1) len1--;//去除前导0 
    for(int i=0;i<len1;i++){    
        cout<<ans[len1-i];
    }
    return 0;
}

是否输出余数看自己要求添加。

2.高精度除以高精度
学不明白,等学明白了再补上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值