思路简单做起来容易出错的大整数四则运算
2014-03-28 10:24
423 查看
如题思路简单做起来容易出错的大整数四则运算,
加减乘的思路比较相似,先对应为加减乘后处理进位或借位,
加法和减法也可以带着进位边加减边处理进位 ,乘法用一个错位的循环刚好是我们手算得过程,所以最后处理进位要方便些,
对于除法想了一会,上网搜了看大都是模拟手算的过程,移位后对应位做减法 ,减到负数为止,减法的次数-1就是对应位的商值 跳出外循环的条件是余数小于除数
写了一整个下午才写出来。。。动手能力有待提高。基本上一个运算符一个小时,除法做的最久。。。
以后多写几次 速度应该会提高吧。
开始没有写 类 直接写的几个函数 后来为了响应课堂所学的面向对象 把它封装到了一个bigNum类中。。
参考网页
大数乘法
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
加减乘的思路比较相似,先对应为加减乘后处理进位或借位,
加法和减法也可以带着进位边加减边处理进位 ,乘法用一个错位的循环刚好是我们手算得过程,所以最后处理进位要方便些,
对于除法想了一会,上网搜了看大都是模拟手算的过程,移位后对应位做减法 ,减到负数为止,减法的次数-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
相关文章推荐
- biztalk 2006 安装详细截图,相对安装过程复杂容易出错的biztalk2004来讲,安装算是非常简单了
- 简单基础的问题,但是非常容易出错.
- C#实现的简单整数四则运算计算器功能示例
- 利用递归下降分析法求解简单正整数四则运算
- java开源框架应用技巧之spring配置文件中如果有多个.hbm.xml文件的话,无论是项目开发过程中还是维护过程中修改起来都会很麻烦切容易出错
- 简单 但 容易出错的地方
- define只是简单替换,所以不要用函数,或条件表达式,容易出错
- 关于静态数据成员的简单却容易出错的小程序
- 一道简单而又容易出错的题目
- 一道简单而又容易出错的题目
- 一道简单而又容易出错的题目
- ZOJ 1312 题解这是一道简单题但容易出错,不需要使用任和算法就可做出来,而且很快。我的AC 0ms
- 选择类型控件[控件虽然好学,用起来还是不是那么容易的~!]
- iOS 最简单解决事件冲突的思路,深入浅出cancelsTouchesInView属性
- Java简单实现爬虫技术,抓取整个整个网站所有链接+图片+文件(思路+代码)
- 简单编程(十四)定义一个方法能够判断并返回两个整数的最大值,并调用自己的方法测试是否正确。
- Find a way bfs搜索 容易出错
- 华为软件编程题:简单的四则运算
- 浅谈装饰者模式的简单使用和思路
- unity 关于打包androidjar并调用android jar包开发打电话功能的一些容易出错的点