您的位置:首页 > 其它

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

2015-06-19 05:43 288 查看
高精度的十进制运算处理一

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的位数则用公式:[
a3f6
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).
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: