高精度运算
——选自《算法竞赛入门经典》
当运算结果非常大时,需要涉及到“高精度算法”,用数组来存储整数。在《算法竞赛入门经典》一书中,提到了是3个例子。
1.小学生算术
(1)问题描述
好多小学生在学习加法运算时,经常把“进位”搞错。现在需要设计一个算法,计算两个整数相加时,需要多少次进位。程序应当可以连续处理多组数据,直到读到两个0(输入结束标志)。假设输入的整数都不超过9位数。
样例输入:
123 456
555 555
123 594
0 0
样例输出:
0
3
1
(2)问题分析
int 数据类型一般都是4个字节,可以表示的最大整数约是2,000,000,000(232/2)。因此可以表示9位数。可以通过模10获取一个数的个位数。
(3)代码
#include<stdio.h>
int main(){
int a,b;
while(scanf("%d %d",&a,&b)==2){
if(!a && !b)
return 0;
int c=0,ans=0;
for(int i=9;i>=0;i--){
c = (a%10+b%10+c)>9? 1:0;
ans += c;
a /= 10;
b /= 10;
}
printf("%d\n",ans);
}
return 0;
}
(4)运行结果
2.阶乘的精确值
(1)问题描述
输入一个不超过1000的正整数n,输出n!的精确结果。
(2)问题分析
为了保存结果,先分析1000!有多大。使用计算器计算,大致为4*102567,因此可以使用一个3000个元素的数组f保存。为了方便起见,使用f[0]保存结果的个位,f[1]保存结果的十位……(逆序保存,方便处理进位)。在输出时,应该忽略前导0。
(3)代码
#include<stdio.h>
#include<string.h>
const int max = 3000;
int f[max];
int main(){
int i,j,n;
scanf("%d",&n);
memset(f,0,sizeof(f));
f[0]=1;
for(i=2;i<=n;i++){
//乘以i
int c = 0;
for(j=0;j<max;j++){
int s = f[j] * i + c; //相乘
f[j] = s % 10; //取最低位,记录
c = s / 10; //进位
}
}
for(j = max-1;j>=0;j--){ //忽略前导0
if(f[j])
break;
}
for(i=j;i>=0;i--){
printf("%d",f[i]);
}
printf("\n");
return 0;
}
(4)运行结果
3.高精度运算类——bign
上面的方法虽然可以实现高精度运算,但是代码难以实现复用。因此可以考虑写一个“高精度函数库”,方便调用。基于此,设计一个结构体bign来储存高精度非负整数。话不多说,直接上代码。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1000;
struct bign{
int len;
int s[maxn];
bign(){
memset(s,0,sizeof(s));
len = 1;
}
bign (int num){
*this = num;
}
bign (const char *num){
*this = num;
}
bign operator= (const char* num){
len = strlen(num);
for(int i=0; i<len; i++){
s[i] = num[len-i-1] - '0'; //'0'的ASCII值为48,此处是将字符例如'1'转为数字1
}
return *this;
}
bign operator= (int num){
char s[maxn];
sprintf(s,"%d",num);
*this = s; //此处的“=”已经被重载
return *this;
}
string str() const{
string res = "";
for(int i = 0; i < len; i++){
res = (char)(s[i] + '0') + res;
}
if(res == ""){
res ="0";
}
return res;
}
bign operator + (const bign& b) const{
bign c;
c.len = 0;
for(int i=0,g=0; g || i<max(len, b.len); i++){ //条件运算符的优先级大于逻辑运算符的优先级
int x = g;
if(i < len){
x += s[i];
}
if(i < b.len){
x += b.s[i];
}
c.s[c.len++] = x % 10;
g = x / 10; //进位
}
return c;
}
bign operator += (const bign& b){
*this = *this + b;
return *this;
}
bool operator < (const bign& b) const{
if(len != b.len){
return len < b.len;
}
for(int i=len-1; i>=0; i--){
if(s[i] != b.s[i]){
return s[i] < b.s[i];
}
}
return false;
}
bool operator > (const bign& b) const{
return b < *this;
}
bool operator <= (const bign& b) const{
return !(b < *this);
}
bool operator >= (const bign& b) const{
return !(*this < b);
}
bool operator != (const bign& b) const{
return b<*this || *this<b;
}
bool operator == (const bign& b) const{
return !(b<*this) && !(*this<b);
}
};
istream& operator >> (istream &in, bign& x){
string s;
in >> s;
x = s.c_str(); //c_str()是string的方法,将C++的string型数据转换为C的字符串型,
return in;
}
ostream& operator << (ostream &out, const bign& x){
out << x.str();
return out;
}
int main(){
bign a,b,c;
bool d;
a = 1234;
b = 4567;
c = a+b;
d = a < b;
cout<<c<<endl;
cout<<d<<endl;
return 0;
}
运行结果:
由于C++是面向对象的,而且C++几乎已经完全兼容了C,相比C,C++具有强大的优越性,所以使用了C++来写代码。
4.总结
总体来说的话,这3份代码理解起来不难,没有用到多么深奥的结构与语法。但是总感觉人家设计的非常巧妙,思路非常的流畅。这种思考与处理问题的思想,也许是我们应该更深层次,站在更高的角度要不断探索与学习的吧!记得上算法课的时候,算法老师,也就是我们院长,总是说学算法贵在学思想,我们是学计算机的,要刻意地培养与训练计算思维。感觉这里把计算思维发挥的淋漓尽致。与诸君共勉!