五种高精度算法

五种高精度算法

高精度运算就是指数据超过int(1e8),long long(1e18)等类型的数之间的运算,主要包括高精度加法,高精度减法,高精度乘法,高精度除以低精度,高精度除以高精度

高精度加法

模拟加法进位
高精度加法比较简单,用数组来存储每个数,对应位相加,如果大于10,就向前一位进1。

#include<iostream>
using namespace std;
//高精度加法
//洛谷p1601
int a[510],b[510],c[510];
int la,lb,lc;  //记录每个数的长度
int main()
{
    string s1,s2;
    cin>>s1>>s2;
    //每个数的长度
    la = s1.length();
    lb = s2.length();
    //转置  方便计算进位
    for(int i=0;i<la;i++)
    a[la-i] = s1[i] - '0';
    for(int i=0;i<lb;i++)
    b[lb-i] = s2[i] - '0';
    //预判lc的值 比最大的大1
    lc = max(la,lb) + 1;
    //开始相加
    for(int i=1;i<=lc;i++)
    {
        //这里是+=因为c[i]有可能在上一步被进位了
        c[i] += a[i] + b[i];
        c[i+1] = c[i]/10;   //进位
        c[i] = c[i]%10;
    }
    //除去前面的0 
    //if判断(也可以是while但是条件要变为lc>1)保证当结果为0时可以输出0
    //在加法里lc最多只会比实际结果位数大1
    if(c[lc]==0&&lc>0) lc --;
    for(int i=lc;i>0;i--)
    cout<<c[i];
    return 0;
}

高精度减法

模拟减法借位
高精度减法跟加法差不多,用数组存储数据,对应位向减,如果不足,就向前一位借1。

#include<iostream>
using namespace std;
//高精度减法
//洛谷p2142
int a[10090],b[10090],c[10090];
//判断a,b的大小关系
bool compara(int *a,int *b)
{
    if(a[0]!=b[0]) return a[0]>b[0];
    for(int i=a[0];i>=1;i--)
    {
        if(a[i]!=b[i]) return a[i]>b[i];
    }
    return true;
}
int main()
{
    string s1,s2;
    cin>>s1>>s2;
    //转置
    a[0] = s1.length();  //用数组的0位来记录数组的长度
    b[0] = s2.length();  //也可以像上面那样用la,lb,lc
    //预判c的长度 不会超过最大的
    c[0] = max(a[0],b[0]);
    //转置
    for(int i=0;i<s1.length();i++)
    a[a[0]-i] = s1[i] - '0';
    for(int i=0;i<s2.length();i++)
    b[b[0]-i] = s2[i] - '0';
    //标记结果的符号
    int flag = 0;
    //保证a大于b 
    if(!compara(a,b)) //当a小于b时
    {
        flag = 1;  //结果为负
        for(int i=0;i<=c[0];i++)
        swap(a[i],b[i]);   //交换a,b
    }
    //开减
    for(int i=1;i<=a[0];i++)
    {
        if(b[i]>a[i])
        {
            a[i+1] --;  //借位
            a[i] += 10;
        }
        c[i] = a[i] - b[i];
    }
    //除去前置0
    while(c[c[0]]==0&&c[0]>1) c[0] --;
    if(flag) cout<<'-';
    for(int i=c[0];i>0;i--)
    cout<<c[i];
    return 0;
}

高精度乘法

模拟乘法运算
高精度乘法也是用数组来记录每个数据,根据乘法运算的规律,每一位跟另一个数相乘,对应位再相加,大于10就进位。
如图
图片来源于b站麦克老师讲算法
可以看出下标a*b中下标和为同一个数sum的所有数的和就是c中对应sum-1位置的值
也就是c[i+j] += a[i]+b[j]

#include<iostream>
using namespace std;
//高精度乘法
//信息奥赛一本通 1174
string s1,s2;
int a[500],b[500],c[500];
int la,lb,lc;
int main()
{
    cin>>s1>>s2;
    la = s1.length();
    lb = s2.length();
    //转置 同样因为涉及到进位
    for(int i=0;i<la;i++)
    a[la-i] = s1[i] - '0';
    for(int i=0;i<lb;i++)
    b[lb-i] = s2[i] - '0';
    //预判lc的值 最大为la+lb
    lc = la+lb;
    //相乘
    for(int i=1;i<=la;i++)
    {
        for(int j=1;j<=lb;j++)
        {
            //核心思想
            c[i+j-1] += a[i]*b[j];
            c[i+j] += c[i+j-1]/10;  //进位
            c[i+j-1] %= 10;
        }
    }
    //除去前置0
    //预判的lc最大比真实结果大1 最多只需要减一次 
    if(c[lc]==0&&lc>0) lc --;
    for(int i=lc;i>0;i--)
    cout<<c[i];
    return 0;
}

高精度除以低精度

模拟除法运算
高精度除以低精度就是模拟除法,在被除数的每一位上记录与除数相乘小于这一位对应的余数的数的最大值,如图
(来源于b站麦克老师讲算法)
取被除数4567的第一位4与23相除,得到0,再用余数4*10+5与23相除得到结果的第二位1
以此类推可以得到结果
c[i] = (x*10+a[i])/b

x =(x*10+a[i] )%10
c[i]指结果中第I位,a是被除数 ,b是除数,x是余数

代码如下

#include<iostream>
#include<cstring>
using namespace std;
//高精度除法——高精度除以低精度
//洛谷p1480
char s[50005];
//因为题目中的除数是1e9 在计算中可能会超过int 使用long long
//c记录结果 x记录余数
long long a[50005],b,x,c[50005];  //全局变量会自动初始化为0
int la,lc;
int main()
{
    cin>>s;
    cin>>b;
    la = strlen(s);
    //不用转置 除法就是从最高位开始且不涉及进位
    for(int i=1;i<=la;i++)
    a[i] = s[i-1] - '0';
    //预判除数是la位(最大为la)
    for(int i=1;i<=la;i++)
    {
        c[i] = (x*10+a[i])/b;  //除数的第i位
        x = (x*10+a[i])%b;   //余数
    }
    //除去前置0 从前到后
    lc = 1;
    //lc<la不加等号 是为了保证当被除数大于除数时可以输出一个0
    while(c[lc]==0&&lc<la) lc ++;
    for(int i=lc;i<=la;i++)
    cout<<c[i];
    return 0;
}

高精度除以高精度

模拟用高精度减法实现除法
高精度之间的除法,比较麻烦,不能用除法来计算了,利用除法的原理减法来进行并且是高精度减法,除法的来源就是被除数减去n倍的除数得到的余数小于除数即可,我们就可以模拟这一过程来实现,如图(来源于b站麦克老师讲算法)
为了方便就让除数向前移位与被除数对齐来实现减法过程(这一步就是指被除数的前n位与除数不断向减,相当于把前n位取出,如果除数位数为n的话),直到余数小于除数,而减的次数就是结果对应位的那个数请添加图片描述
计算除数每次移动的位数
用a[0]记录被除数的长度,b[0]记录除数的长度,
则结果的长度最大为c[0] = a[0] - b[0] + 1
而每次移动的长度其实是a[0] - b[0],又因为涉及到高精度减法所以在存储数据时是倒序的c[i] 中的下标i 就是从1到c[0]递增的
记录结果时也是for(int i=c[0];i>0;i++) 倒序进行的
那么移动的位数就是对应c中下标i-1的值

#include<iostream>
#include<cstring>
using namespace std;
//高精度除法——高精度除以高精度
//减法模拟除法
//信息学奥赛一本通c++版p1308
int a[305],b[305],c[305],tmp[305]; //tmp记录每次的移位后的除数
//实现除数向左移n位
void numcopy(int *a,int *b,int n)
{
    for(int i=1;i<=a[0];i++) 
    b[n+i] = a[i];
    b[0] = a[0] + n;  //更新b的长度
}
//比较当前余数跟当前除数的大小关系
int compara(int *a,int *tmp)
{
    if(a[0]>tmp[0]) return 1; 
    if(a[0]<tmp[0]) return -1;
    for(int i=a[0];i>=1;i--)
    {
        if(a[i]>tmp[i]) return 1;
        if(a[i]<tmp[i]) return -1;
    }
    return 0;
}
//实现高精度减法
void minu(int *a,int *tmp)
{
    for(int i=1;i<=a[0];i++)
    {
        if(a[i]<tmp[i])
        {
            a[i+1] --;
            a[i] += 10;
        }
        a[i] = a[i] - tmp[i];  //向减的结果又再次赋值给了a a存储的就是余数
    }
    //更新a的位数 除去前置0
    //>1 可以保证0可以输出
    while(a[a[0]]==0&&a[0]>1) a[0] --;
}
int main()
{
    //输入除数和被除数
    string s1,s2;
    cin>>s1>>s2;
    a[0] = s1.length();
    b[0] = s2.length();
    //因为是用减法实现,所以需要转置,有借位运算
    for(int i=0;i<a[0];i++) a[a[0]-i] = s1[i] - '0';
    for(int i=0;i<b[0];i++) b[b[0]-i] = s2[i] - '0';
    //预判结果c的最大长度
    c[0] = a[0] - b[0] + 1;
    //计算结果的各个位上的值
    for(int i=c[0];i>0;i--)
    {
        memset(tmp,0,sizeof(tmp));  //每次都要对中间变量tmp清0
        numcopy(b,tmp,i-1);     //对除数进行移位 i-1
        while(compara(a,tmp)>=0)     //高精度减法 
        {
            c[i] ++;
            minu(a,tmp); 
        }
    }
    //除去c的前置0 
    while(c[c[0]]==0&&c[0]>1) c[0] --;
    //输出结果
    for(int i=c[0];i>0;i--) cout<<c[i];
    cout<<endl;
    //输出余数
    for(int i=a[0];i>0;i--) cout<<a[i];
    return 0;
}

好了,就是这些,本人表达能力不行而且是个菜鸟,也是刷到高精度的题不会去b站上看视频学的,如果有任何说的不对的地方,希望大佬们可以帮我指出来,我一定积极改正的。如果没看懂的话,可以去看看b站麦克老师讲算法的那个视频,讲的很好,本文里的图片都出自麦克老师讲算法。

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值