ACM选修(高精度算术运算)

高精度的十进制运算处理一

1.由于待处理的数据超过了任何一种数据类型所能容纳的范围,因此必须采用数字串的形式进行输入,并将其转化为整数数组。
该数组的每一个元素对应一位十进制数,有其下标顺序指明位序号。
运算规则如同算术运算。
缺点
整数数组-每个单元存储才一个数字位却以整数类型来申请空间。
空间浪费严重。
读入不方便。
2.由于待处理的数据超过了任何一种数据类型所能容纳的范围,因此必须采用数字串的形式进行输入,并将其转化为字符数组。
该数组的每一个元素对应一位十进制数,有其下标顺序指明位序号。
运算规则如同算术运算。
由于高精度运算的结果可能使得数据长度发生增减,因此除了需要用字符数组来存放数据外,还需要用一个整数变量来记录字符数组的元素个数,即数据的实际长度。
优点
字符数组——每个单元均以8位来存储一个数字位。
空间节省。
读入方便。
用字符串模拟大整数
->结论
读入问题:字符串读入
计算问题:每位字符按位模拟,通过与ASCⅡ码之间的对应关系完成字符与数字之间的协调转换

实现方法
#define max 500
typedef int Arr[max];//整数数组类型
Arr a,b;//整数数组a,b
int la,lb;//分别存放整数数组a,b的实际长度
char *str//所输入的数字串
将数字串的各位数字存入整数数组的算法为:k=strlen(str);
(1)for (j=0;j<k;j++) a[k-j-1]=str[j]-48;//从低位到高位进行存放
(2)for (j=0;j<k;j++) a[j]=str[j]-48;//从高位到低位进行存放

//算法1.a=a+b(a,b均为高精度数
//思路:由低为到高位的顺序进行加法运算。
void  Plus(Arr a,Arr b, int &la,int lb) 
{ 
int I ,x; 
if (la>=lb) x=la; else x=lb;    
for (I=0;I<x I++)//逐位相加
{ 
    a[I]=a[I]+b[I]; 
    a[I+1]=a[I+1]+a[I] /10;//a[I]/10是第I位向第I+1位的进位
    a[I]=a[I]%10;                  
} 
while (a[x+1]!=0) x=x+1;     
la=x; 
}
//算法2:a=a-b(a,b均为高精度数
//思路:由低位向高位的顺序进行减法运算。
void  minus(Arr a,Arr b,int &la,) 
{ 
int I; 
for (I=0;I<la I++)//逐位相减
{ 
     if (a[I]<b[I])//不够减则要借位
     { 
          a[I+1] --; 
          a[I]=a[I]+10; 
     } 
    a[I]=a[I]-b[I];//计算出差的第I位
} 
while (a[la]==0) la--;//计算出差的实际长度
} 
//算法3:a=a*c(a为高精度数,c为一个整数
//思路:从低位向高位的顺序进行乘法运算
void  Mul(Arr a,int &la,int c) 
{ 
int I  
a[0]=a[0]*c;//第一位初始化
for (I=1;I<la I++)//逐位相乘
{ 
    a[I]=a[I]*c; 
    a[I]=a[I]+a[I-1] /10;//a[I]/10是第I位向第I+1位的进位
    a[I-1]=a[I-1]%10;                  
} 
while (a[la]>=10)//积的最高为进位
{  
   la=la+1;    
   a[la]=a[la-1]/10; 
   a[la-1]=a[la-1]%10; 
} 
}
//算法4:c=a*b(a,b,c均为高精度数
int c[2*max+1];//由于a.b的位数最高为max,因此c应为2*max+1位
//思路:从低位向高位的顺序进行乘法运算  
void mul(Arr a[], Arr b,Arr c,int la,int lb,int &lc)  //乘法运算
{//利用高精度乘法求出a*b,并将结果存入c中。
    int i,j;
for (int k=0;k<2*max;k++) 
c[k]=0; 
for( i=0  i<la  i++) 
       for( j=0  j<lb  j++)
   c[i+j]+=a[j]*b[i];//逐位逐位求
    for( i=0  i<la  i++) 
{ c[i+1]+=c[i]/10;//求进位,即求出第i位向i+1位的进位
c[i]=c[i]%10;  
} 
} 
   lc=2*max; 
while (c[lc]==0) lc--;//计算出积的实际长度
}  
//算法5:a=a/I(I为一个整数
//思路:从高位向低位的顺序进行乘法运算
void div(Arr a, int &la, int I) 
{ 
   m=0;//余数初始为0 
   for ( j=la-1; j>=0;j--)//逐位相除
   { 
      a[j]+=m*10;//接受来自j+1位的余数
      m=a[j] %I;//计算第j位的余数
      a[j]=a[j]/I;//计算商的第j位
} 
while (a[la-1]==0) la--;//计算出商的有效位数
}
->高精度运算的改进思路
由于前面的算法都是使用数组的一个元素来表示一位十进制数字,因此当该高精度数的位数很多时,则对应的数组长度也会很长,并增加了计算的时间。
解决方法是可以考虑使用数组的一个元素来存放高精度数的2位或3位等等。
其对应的加、减、乘等算法与前面的算法比较相似,请自行改进。

问题:Mason数(麦森数)
问题描述:形如2p-1的素数称为Mason数,这时p也一定是个素数。但反过来则不一定,即如果p是个素数,2p-1不一定也是素数。到1998年底,人们已找到了37个Mason数。Mason有许多重要的应用,它与完全数密切有关。现要求大家编写一个程序:输入一个素数p(1000<p<3100000),计算出2p-1的位数和最后500位数字。
说明:不用验证2p-1与p是否为素数。
要解决的两个问题
(1)判断素数,不用解决
(2)求出2的p次方的位数;
(3)求出2的p次方的最后的500位。
A.第一个问题 由于一个自然数n的位数等于[lgn]+1,因此,为了求出一个自然数n的位数,我们只需要使用lg函数即可,但又由于C语言只提供ln对数,因此我们可以使用logn=lnn/ln10即可,而要计算2p的位数则用公式:[p*ln2/ln10]+1即可
B.第二个问题 由于要求输出2p -1最后的500位, 因此我们只能采用高精度算法逐位逐位进行计算。
计算方法是可以通过计算2[ p/2 ] 的平方(如果是奇数,还要再乘以2)来得到。采用该算法,其时间复杂度就为O(log2p*500*500)。这样的算法效率就高了。对于该算法还可以进一步优化:对于一个数,设为(a1a2a3…a250a251,…a500),其平方的后500位可以这样计算:     (a1a2a3…a250a251,…a500)2的后500位等于2*((a1a2a3…a250)* (a251,…a500)*10250+( a251,…a500)2,这样,算法的时间复杂度可以减少一半,为O(log2p*2*2502).

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值