算法学习-高精1-高精度加法和高精度减法

整数的高精度加、减法的实现

由于C++不自带高精,所以常常无法处理一些大数的运算。最近我学习了高精度,将所学内容整理于此。其实是为了应付这周五的学习笔记(我刚接触C++,也是第一次用博客,,如果有什么错误请多多包涵,,)


  • 为什么要用高精度?
    因为long long最大也只能存20位的数,强行输入大数会爆 掉

  • 那要怎么实现高精度计算?
    那就用用字符数组或string类存储大数,然后根据小学生加减法原理进行加减乘除,得出结果。


    接下来先对最简单的高精度加法进行实现。

整数的高精度加法 (负数见减法部分)

 先从离我们最近的入手:小学生加法原理
  我们计算\(a_1b_1c_1d_1+b_2c_2d_2\)时,先是个位\(d_1+d_2\),满\(10\)\(1\),可以理解为满\(10\),就让\(d_1+d_2\)的结果减去\(10\),记为个位,下一次计算\(c_1+c_2\)时就同时加上一个\(1\),如果满\(10\)同上操作,记为十位。如果都不满\(10\),那就不减\(10\)也不进\(1\)。其他依次往前推。
  这样我们就不难得出整个算法的核心:

c for (i=0;i<=c3;++i)
    {
        c[i]+=a1[i]+a2[i];   //a1,a2用来储存两个相加的数
        c[i+1]=c[i]/10;   //进位的实现
        c[i]=c[i]%10;
    }

那具体要怎么实现?
输入部分不必多说,输入后我们必须先知道两个字符数组的最长的长度,由此来知道进行加法的最高位数,这也是字符数组的便利所在:

c1=strlen(s1);
c2=strlen(s2);
len=max(c1,c2);

之后将字符数组逆序赋值给整型数组,为什么呢?()

for (i=0;i<c1;++i)
        a1[i]=s1[c1-1-i]-'0';//要减去个‘0’

for(i=0;i<c2;++i)
        a2[i]=s2[c2-1-i]-'0';

之后就可以进行加法啦!

for (i=0;i<=len;++i)//这里len为什么要用等号?
{
     c[i]+=a1[i]+a2[i];
     c[i+1]=c[i]/10;
     c[i]=c[i]%10;
}

然后逆序输出,记得删除前导0

if (c[len]==0) --len;//删除前导0

for (i=len;i>=0;--i)
    cout<<c[i];

这样就大工造成啦!
附上我自己第一次做时候的代码:

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char s1[505]={0},s2[505]={0};
    int a1[505]={0},a2[505]={0},c[506]={0};
    int i=0,c1=0,c2=0,c3=0;

    cin>>s1>>s2;

    c1=strlen(s1);
    c2=strlen(s2);
    c3=max(c1,c2);

    for (i=0;i<c1;++i)
        a1[i]=s1[c1-1-i]-'0';

    for(i=0;i<c2;++i)
        a2[i]=s2[c2-1-i]-'0';

    for (i=0;i<=c3;++i)
    {
        c[i]+=a1[i]+a2[i];
        c[i+1]=c[i]/10;
        c[i]=c[i]%10;
    }

    if (c[c3]==0)
    {
        for (i=c3-1;i>=0;--i)
            cout<<c[i];
    }
    else
    {
        for (i=c3;i>=0;--i)
            cout<<c[i];

    }
  return 0;
}


接下来是小学生减法(结果负数也得输出,以下用string做答)

也先从原理入手:
嘛,,竖式减法,如果\(\ a_1b_1c_1d_1-b_2c_2d_2\),先个位相减,也就是\(\ d1-d2\),若小于\(\ 0\),则让\(\ c_1\)减去\(\ 1\),再把\(\ d1+d2\)加上10,记为个位,然后进行\(\ c_1-c_2\),如果结果大于\(\ 0\),就直接记为十位,再进行\(\ b_1-b_2\),依次往前推。
这样可以得出:

for (i=0;i<lim;++i)//lim为两个数中最高位的位数
    {
        ans[i]+=arr1[i]-arr2[i];
        if (ans[i]<0)
        {
            --ans[i+1];
            ans[i]+=10;
        }
    }

我用 --ans[i+1] 来实现退位。

然而这只是应付arr1中存的数比arr2中大的情况,那么:
当arr1<arr2时,用swap进行交换,并用 bool flag 记录结果正负

if (( n1=str1.size())==(n2=str2.size())&&str1<str2||n1<n2)
    {
        str1.swap(str2);//保证str1>=str2
        flag=1;
    }

这样就可以随意进行任意整数的加减了,其余部分基本与加法一致。

搭嘎!删除前导0这里有坑:

  • 和加法不一样,减法的最高位数可能是多个0,可以用循环进行删除:
while (ans[lim-1]==0) lim--;
  • 如果结果等于 0 ,则要特殊考虑:
if (lim-1<0) cout<<0;

贴代码:

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main()
{
    string str1,str2;
    int n1=0,n2=0,lim=0,i=0;//n用来存字符串长度
    bool flag=0;
    int arr1[10500]={0},arr2[10500]={0},ans[10500]={0};

    cin>>str1>>str2;

    if (( n1=str1.size())==(n2=str2.size())&&str1<str2||n1<n2)
    {
        str1.swap(str2);//保证str1>=str2
        flag=1;
    }

    n1=str1.size();
    n2=str2.size();

    for (i=n1-1;i>=0;--i) arr1[n1-1-i]=str1[i]-'0';
    for (i=n2-1;i>=0;--i) arr2[n2-1-i]=str2[i]-'0';

    lim=max(n1,n2);

    for (i=0;i<lim;++i)
    {
        ans[i]+=arr1[i]-arr2[i];
        if (ans[i]<0)
        {
            --ans[i+1];
            ans[i]+=10;
        }
    }

    while (ans[lim-1]==0) lim--;

    if (flag==true) cout<<'-';

    if (lim-1<0) cout<<0;

    for (i=lim-1;i>=0;--i)
        cout<<ans[i];



return 0;
}

顺带一提,我之前用字符数组实现高精减法的是这个的2倍长


写完之后感觉格式好乱,,,


高精度减法的实现我参考了这个
别人说的蛮详细的,,不像我,,很多地方都懒得解释

posted @ 2020-11-04 17:39  七铭的魔法师  阅读(263)  评论(0编辑  收藏  举报