思路简单做起来容易出错的大整数四则运算

如题思路简单做起来容易出错的大整数四则运算,

加减乘的思路比较相似,先对应为加减乘后处理进位或借位,

加法和减法也可以带着进位边加减边处理进位 ,乘法用一个错位的循环刚好是我们手算得过程,所以最后处理进位要方便些,

对于除法想了一会,上网搜了看大都是模拟手算的过程,移位后对应位做减法 ,减到负数为止,减法的次数-1就是对应位的商值 跳出外循环的条件是余数小于除数  

写了一整个下午才写出来。。。动手能力有待提高。基本上一个运算符一个小时,除法做的最久。。。闭嘴以后多写几次 速度应该会提高吧。

开始没有写 类 直接写的几个函数 后来为了响应课堂所学的面向对象 把它封装到了一个bigNum类中。。

#include <iostream>
using namespace std;

class bigNum
{
    public:
    string result,big;
    string add(const string &a_,const string &b_);
    string sub(const string &a_,const string &b_);
    string mul(const string &a_,const string &b_);
    string div(const string &a_,const string &b_);
    string operator + (const bigNum &B_);
    string operator - (const bigNum &B_);
    string operator * (const bigNum &B_);
    string operator / (const bigNum &B_);

};
string  bigNum::operator + (const bigNum &B_)
{
    add(big,B_.big);
    return result;
}
string  bigNum::operator - (const bigNum &B_)
{
    sub(big,B_.big);
    return result;
}
string bigNum::operator * (const bigNum &B_)
{
    mul(big,B_.big);
    return result;
}
string  bigNum::operator / (const bigNum &B_)
{
    div(big,B_.big);
    return result;
}


string bigNum::add(const string &a_,const string &b_)
{
    string a,b,c_tem;
    int *tem;
    char *re;
    unsigned i,j;
    a = a_ ;b = b_;
    if(a.length() < b.length()){c_tem = a; a = b; b = c_tem;}
    tem = new int[a.length() + 1];//结果最大为较长的数的长度加1
    re = new char[a.length() + 2];//存放中间结果的字符串 多一个用了存放 '\0'
    for(i = 0;i < a.length() + 1;i++)tem[i] = 0;//数组初始化
    for(i = 0;i < a.length() + 2;i++)re[i] = '\0';

    for(i = 0,j = 0;i < a.length();i++)
    {
        if(i >= (a.length() - b.length()))
            tem[i+1] = (a[i] - 48) + (b[j++] -48);
        else tem[i+1] = a[i] - 48;
    }
    for(i = a.length();i > 0;i--)//这个地方必须从低位开始处理进位
    {
        if(tem[i] >= 10)
        {
            tem[i-1] += 1;
            tem[i] -= 10;
        }
    }
    i = 0;
    while(tem[i] == 0)i++;//找到第一个不为0的位置
    for(j = 0;i < a.length() + 1;i++,j++)//跳过多少个0 相应的结果就少几位
        re[j] = tem[i] + 48;
    result = re;
    delete []re;
    delete []tem;
    return result;
}


string bigNum::sub(const string &a_,const string &b_)
{
    string a,b,c_tem;
    unsigned sign = 0,i,j;
    int *tem ;
    char *re ;
    a = a_; b = b_;
    if(a.length() < b.length()){c_tem = a; a = b; b = c_tem;sign = 1;}
    else if(a == b){result = "0";return result;}
    else if(a.length() == b.length() && a < b){c_tem = a; a = b; b = c_tem;sign = 1;}

        tem = new int[a.length()];
        re = new char[a.length() + 1];//存放 负号的位置
        for(i = 0;i < a.length();i++)tem[i] = 0;
        for(i = 0;i < a.length() + 1;i++)re[i] = '\0';

        for(i = 0 ,j = 0;i < a.length();i++)
        {
            if(i >= (a.length() - b.length()))tem[i] = a[i] - b[j++];
            else
            tem[i] = a[i] -48;
        }

        for(i = a.length() -1;i > 0 ;i--)//和加法一样 处理借位
        {
            if(tem[i] < 0)
            {
                tem[i-1] -= 1;
                tem[i] += 10;
            }
        }
         i = 0;
         while(tem[i] == 0)i++;//过滤掉 零
         if(sign)
         {
             for(j = 1; i < a.length();i++,j++)re[j] = tem[i] + 48;
             re[0] = '-';
         }
         else
             for(j = 0; i < a.length();i++,j++)re[j] = tem[i] + 48;

        result = re;

    delete []tem;
    delete []re;
    return result;
}


string bigNum::mul(const string &a_,const string &b_)
{
    unsigned  i,j;
    string a,b,c_tem;
    int *tem;
    char *re;
    a = a_,b = b_;
    tem = new int[a.length() + b.length()];
    re = new char[a.length() + b.length() + 1];
    for(i = 0;i < a.length() + b.length();i++)tem[i] = 0;
    for(i = 0;i < a.length() + b.length() + 1;i++)re[i] = '\0';
    for(i = 0;i < a.length();i++)
        for(j = 0;j < b.length();j++) //乘法的竖式计算过程
            tem[i + j + 1] += (a[i] - 48) * (b[j] - 48);

        for(i = a.length() + b.length() - 1;i > 0;i--)//处理进位
        {
            if(tem[i] >= 10)
            {
                tem[i-1] += tem[i] / 10;
                tem[i] %= 10;
            }
        }
        i = 0;
        while(tem[i] == 0)i++;
        for(j = 0;i < a.length() + b.length();i++,j++)
            re[j] = tem[i] + 48;

    result = re;
    delete []re;
    delete []tem;
    return result;
}

string bigNum::div(const string &a_,const string &b_)
{
    string a,b,c_tem,mod,test;
    char *re;
    unsigned i,len_a,len_b;
    int tem = -1,n_re = 0,k = 0;
    a = a_; b = b_;
    if(a.length() < b.length()){result = "0";return result;}
    else if(a.length() == b.length())
            {
                if(a < b)
                {
                    result = "0";
                    return result;
                }
                else if(a == b)
                {
                    result = "1";
                    return result;
                }
            }
    re = new char[a.length() - b.length() + 2];
    for(i = 0; i < a.length() - b.length() + 2;i++)re[i] = '\0';
    len_a = a.length();len_b = b.length();
    while(1)
    {
        b = b_;
        mod = a;
        for(i = 0;i < len_a - len_b - k;i++)b.insert(i + len_b ,"0");//b左移补零
        while(1)
        {
        c_tem = mod;
        mod = sub(mod,b);
        tem++;
        if(mod[0] == '-')
        {
            re[n_re] = tem + 48;
            a = c_tem;
            n_re++;
            k++;
            tem = -1;
            break;
        }

        }
    test = sub(a,b_);//退出条件
    if(test[0] == '-' || test == "0")break;

    }
    for(i = n_re;i < len_a - len_b + 1;i++) re[i] = '0';
    result = re;

    delete []re;
    return result;

}

int main()
{
    char x;
    bigNum A,B;
    while(cin>>A.big>>x>>B.big)
    {
         switch(x)
         {
             case '+':cout<<(A + B);break;
             case '-':cout<<(A - B);break;
             case '*':cout<<(A * B);break;
             case '/':cout<<(A / B);break;
         }
        cout<<endl;
   }

    return 0;
}

参考网页

大数乘法
http://sumile.blog.hexun.com/62509182_d.html
http://www.cnblogs.com/hicjiajia/archive/2010/09/26/1836337.html


mooc论坛的程序设计实习讨论

“可以使用类似机器运算除法的方法,实际做的是减法。
假设12345除13,首先找到1300(再扩大10倍就大于12345了),从12345中反复减去1300,9次后不够减,则第一位商是9,再从余数中反复减去130,得到第二位商,以此类推。”


大数除法应该是最难实现的了,假设数组a除以b,实现它有两种方式:
1:用a-=b,循环,直到a<b,记下来执行多少次,则为计算结果;
2:模拟现实,我们手算时是怎么算的,程序就怎么写,
假设a={2 4 2 3 1},b={2 3},结果result={0 0 0 0 0}:
先取a[0]a[1]即24,减去b一次,得a={0 1 2 3 1},result={0 1 0 0 0};
再取a[1]a[2]即12,发现它小于b,则多取一位,取a[1]a[2]a[3]即123,减b五次,得a={0 0 0 8 1},result={0 1 0 5 0};
再取a[3]a[4]即81,减b三次,得a={0 0 0 1 2},result={0 1 0 5 3}。
如果按照方法一计算,效率是无法容忍的。
假如用11111除以1,方法一要计算11111次,而方法二则只要5次。
下面是方法二的代码,经过了简单的测试(没做数据校验,假设数据都是正常的):


http://blog.csdn.net/sunmenggmail/article/details/7532522


加法:O(n)
类似于手算的方法,一个for循环小的那个数,然后每一位对应相加,有进位的就把前一位的数加一
减法:O(n)
同理加法,只是把每一位变成对应相减,如果对应相减得到是负数,该位+10,把前一位减一
乘法:O(n^2)
我是用的手算法,两个for,一个循环乘数一个循环被乘数,用乘数的某一位与被乘数的每一位相乘,结果/10进行进位,%10进行保留
除法:O(nlogn)
这个特别难,我想了2天= =。
用的是这里论坛里另外一个po主的算法,即假设A/B(w.l.o.g. A>B),首先计算两个数的位差即,假设A是10位,B是5位,那么位差为5,讲B向左平移5位(我自己写了一个成员函数,没有重构<<,一开始重构的,但是老错,就改了),然后while一下,用A-B00000直到结果不大于0(一开始用的为负,结果发现在能整除的时候的商的个位数老错,才发现其实可以等于0),记录叠减次数,存进商的位差(5)那一位,然后向左平移4位继续上面的步骤。

可以使用类似机器运算除法的方法,实际做的是减法。
假设12345除13,首先找到1300(再扩大10倍就大于12345了),从12345中反复减去1300,9次后不够减,则第一位商是9,再从余数中反复减去130,得到第二位商,以此类推。






基本的思想是反复做减法,看看从被除数里最多能减去多少个除数,商就是多少。一个


一个减显然太慢,如何减得更快一些呢?以7546 除以23 为例来看一下:开始商为0。先减


去23 的100 倍,就是2300,发现够减3 次,余下646。于是商的值就增加300。然后用646


减去230,发现够减2 次,余下186,于是商的值增加20。最后用186 减去23,够减8 次,


因此最终商就是328。


所以本题的核心是要写一个大整数的减法函数,然后反复调用该函数进行减法操作。


计算除数的10 倍、100 倍的时候,不用做乘法,直接在除数后面补0 即可。


http://www.cnblogs.com/c840136/articles/2168405.html






http://wenwen.soso.com/z/q24755485.htm




http://www.melory.me/2013/03/09/%E5%A4%A7%E6%95%B0%E9%99%A4%E6%B3%95%E8%BF%90%E7%AE%97/


http://www.melory.me/2013/03/09/%E5%A4%A7%E6%95%B0%E9%99%A4%E6%B3%95%E8%BF%90%E7%AE%97/



来个简单的时间分析, 如有不对地方,轻拍。 
+,  没有什么可说的, O( max( m, n)), m, n 分别是 operand1, operand2 的 位数, 这里就100 吧。 
-, 和加一样,  或更间单, 比如, 99999999999999999999999 - 1, 只要二次, 但, 100000000000 - 1, 就要n 次。 
X, 模拟手算 时间是 O( m * n),  m, n 这里最大算一百吧, 时间就时 O(n ^ 2), n = 100.  快一点的“karatsuba algorithms"  O(n ^ ( 3/2)), 比较好实现。 再快的我就不太懂了, 据说能达到O(n*log(n)). 


/, 多种实现方法:
1) 98000000 / 12 =》 98000000 - 1200000 =》 也就是把12 补位后连减, 直到余数小于12。 时间 O(n ^ 2), 因为, 最坏每位数都要减, 如 999999999/1, 每次都用减法, 时间是, O(1 + 2 + 3 + 4 + ... + n ) = O(n ^ 2). 
2) 模拟手算 时间是 O( (n  - m + 1) * m),  n 是被除数的位数长度, m 是除数的位数长度, 如 9999999 / 11, 这里 N = 7, m = 2. 如果m 很小, 会很快, 如果m 很大, 会很快, m = n / 2 时会比较慢。 在求 99 / 11 时就用连减。 
3) 试商是最慢的一种,因为它用到 X, 如用X的时间O(N^2), 那最快的利用试商法 也要 O((n - m) * m * (log( 10 ^ (n - m))), 
因为,假如 n 是被除数的位数长度, m是除数的位数长度, 用binary search 来试商时间也要 O(log(10 ^ (n - m)), 如果m = n / 2, 那就会很慢了, 还有就是没有除法,我真不知如何用 binary search.      如用X的时间是O(N^(3/2)),  运行时间也要, O(( (n - m) * m ) ^ ( 3 /2 ) * (log(10 ^ (n - m))). 
所以除法最好还是被用到乘法。 


https://class.coursera.org/pkupop-001/forum/thread?thread_id=463



我主要利用移位与减法来进行除法运算:
比如123456/12
1.先将12移位4位(6-2=4)变成120000,然后用123456来减,一直减到负数为止;
2.然后将12移位3位,变成12000,然后用第一步减剩下的数(3456)去减12000,一直减到负数为止
3.以此类推,一直到12移位0为止。
大家有没有更简单的方法???
https://class.coursera.org/pkupop-001/forum/thread?thread_id=404

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值