C++实现二进制快速加减法,分治策略之整数位乘(一)

前言

以下是二进制整数位乘中的二进制加法的代码,包含自己尝试与答案分析,虽然二进制加法实现起来非常容易,但是一般要求使用分治实现二进制整数位乘的算法题位数都十分巨大,因此对二进制加法的实现速度也有一定要求。


一、自己写的代码

先看代码,后评价

#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
using namespace std;
struct BigBinary  //用于存储二进制位数
{
    std::vector<int> x;     // 由低位到高位保存二进制位
    bool neg;               // 标记数的正负,true为负
    void Init(){x.clear(); neg = false;}
    BigBinary(){Init();}  //初始化
    void Print()   //打印
    {
        if(neg && !x.empty()) printf("-");
        for(int i = x.size() - 1; i >= 0; i --)
            printf("%d", x[i]);
        if(x.empty()) printf("0");
        printf("\n");
    }
};
BigBinary Add(BigBinary &a, BigBinary &b)
{
// TODO: 返回 a + b 的结果,小心两数异号情况
    if(a.neg!=b.neg)//如果仅需要实现同符号加法请把if和其区域都删去
    {
        if(a.neg)//如果符号不同就转移至减法,并且保证是前减后减法下一篇文章会讲解
        {
            a.neg=!a.neg;
            return Minus(b,a);
        }
        b.neg=!b.neg;
        return Minus(a,b);
    }
    BigBinary c;
    c.Init();
    int k=0; //用于保存是否有进位的情况(k不是0就是1)
    c.neg=a.neg;
    int i=0,j=0,a_long=a.x.size(),b_long=b.x.size(),lin;
    while(i!=a_long&&j!=b_long)  //模拟二进制加法的计算过程
    {
        lin=a.x[i]+b.x[j];
        if(lin==0) //lin用于保存计算结果
        {
            if(k)
            {
                lin++; //若结果为0且k为1
                k--;
            }
            c.x.push_back(lin);
        }else if(lin==1)
        {
            if(k) //这里k不变,lin归0,因为进位
                lin--;
            c.x.push_back(lin);
        }else  //lin==2时
        {
            lin=0;
            if(k)
                lin++;
            else
                k++;
            c.x.push_back(lin);
        }
        i++;
        j++;
    }
    while(i!=a_long) //当b被加完时
    {
        if(k) //如果还存在进位
        {
            if(a.x[i])
            {
                c.x.push_back(0);
                i++;
            }else
            {
                c.x.push_back(1);
                i++;
                k--;
            }
        }else
        {
            c.x.push_back(a.x[i]);
            i++;
        }
    }
    while(j!=b_long) //同理
    {
        if(k)
        {
            if(b.x[j])
            {
                c.x.push_back(0);
                j++;
            }else
            {
                c.x.push_back(1);
                j++;
                k--;
            }
        }else
        {
            c.x.push_back(b.x[j]);
            j++;
        }
    }
    if(k)  //处理二进制边界进位,这里是if而不是while
    //因为二进制相加最多变化位数较多的那位中的一位(二进制之间的关系比如111+111=1110)
        c.x.push_back(1);
    while(!c.x.empty() && c.x.back()==0)
        c.x.pop_back();
    return c;
}
char lin[100005];
int main(void)
{
    BigBinary a,b,c;
    a.Init();
    b.Init();
    scanf("%s",lin);
    for(int i=strlen(lin)-1;i>=0; i--)
    {
        a.x.push_back(lin[i]=='1');
    }
    scanf("%s",lin);
    for(int i=strlen(lin)-1;i>=0; i--)
    {
        b.x.push_back(lin[i]=='1');
    }
    c=Add(a,b);
    c.Print();
}

代码分析:

代码的运行速度勉强达标,题目实现不难,模拟二进制的加法就可以很流程的写出来。但是代码流畅度不够,太繁琐且太注重过程,没有面对对象,并且没有注重二进制加法本身特性。

二.官方代码:(二进制加法)

代码如下:

#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
using namespace std;
struct BigBinary  //用于存储二进制位数
{
    std::vector<int> x;     // 由低位到高位保存二进制位
    bool neg;               // 标记数的正负,true为负
    void Init(){x.clear(); neg = false;}
    BigBinary(){Init();}  //初始化
    unsigned size(){return x.size();}//注意,此处是为了方便直接得到vector大小
    void Print()   //打印
    {
        if(neg && !x.empty()) printf("-");
        for(int i = x.size() - 1; i >= 0; i --)
            printf("%d", x[i]);
        if(x.empty()) printf("0");
        printf("\n");
    }
};
bool AbsAgB(BigBinary &a, BigBinary &b)//注意这里时比较a和b的大小
{
    if(a.size() > b.size()) return true;
    if(b.size() > a.size()) return false;
    for(int i = a.size() - 1; i >= 0; i --)
    {
        if(a.x[i] > b.x[i]) return true;
        if(b.x[i] > a.x[i]) return false;
    }
    return true;
}
BigBinary Add(BigBinary &a, BigBinary &b)
{
    BigBinary c;
    if(a.neg != b.neg)//如果仅需要实现同符号加法请把if和其区域都删去,减法下一篇再讲
    {
        if(AbsAgB(a, b)) //为了让大的减去小的,方便确认结果符号
        {
            b.neg ^= 1;
            c = Minus(a, b);
            b.neg ^= 1;  //因为是引用,所以要转回来,这里是为了改变符号,
        }
        else
        {
            a.neg ^= 1;
            c = Minus(b, a);
            a.neg ^= 1;
        }
        return c;
    }
    if(a.size() < b.size()) return Add(b, a); //为了让位多的在前,可以缩短代码量
    c.x = a.x; //将a的vector赋予c
    for(int i = 0; i < b.size(); i ++) c.x[i] += b.x[i];  //c.x[i]最大的是2
    int cur = 0; //记录进位
    for(int i = 0; i < c.size(); i ++) 
    {
        c.x[i] += cur;  //max(c.x[i])=3
        cur = c.x[i] >> 1; //右移整数和整数/2结果相同,cur最大为1
        c.x[i] &= 1; //这里相当于判断奇偶,偶为0,奇数为1
    }
    for(; cur; cur >>= 1) c.x.push_back(cur & 1); //用if效果一致
    c.neg = a.neg; 
	 while(!c.x.empty() && c.x.back() == 0) c.x.pop_back();
    return c;
}
char lin[100005];
int main(void)
{
    BigBinary a,b,c;
    a.Init();
    b.Init();
    scanf("%s",lin);
    for(int i=strlen(lin)-1;i>=0; i--)
    {
        a.x.push_back(lin[i]=='1');
    }
    scanf("%s",lin);
    for(int i=strlen(lin)-1;i>=0; i--)
    {
        b.x.push_back(lin[i]=='1');
    }
    c=Add(a,b);
    c.Print();
}

代码分析:

整体方面将二进制加法分为三大块,第一部分是全部相加,不统计进位;第二部分是处理二进制数的进位;最后一部分是处理边缘。并且当中利用了很多小细节进行加速,比如 c.x[i] &= 1; 判断奇偶,还有利用右移1位(相当于除以2)来加速。最大的亮点就是不像自己写的代码那样按部就班,注重分块,同时非常简洁条理清晰。

总结

官方写得好,如果发现代码出现bug或有相关疑问都可以在评论区和我讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值