120 likes | 406 Views
算法之三:高精度计算. 在利用计算机进行数值处理时,有时会遇到这样的问题:有些计算要求精度高,希望计算的数的位数可达几十位甚至几百位,虽然计算机的计算机精度也算较高了,但因受到硬件的限制,往往达不到实际问题所要求的精度。我们可以利用程序设计的方法去实现这样的高精度计算。. 高精度计算中需要处理好以下几个问题: ( 1 )数据的接收和存储方法 数据的接收和存储:当输入的数很长时,可采用字符串方式输入,这样可输入数位很长的数,利用字符串函数和操作运算,将每一位数取出,存入数组中。另一种方法是直接用循环加数组的方法输入数据。 ( 2 )高精度数位数的确定
E N D
算法之三:高精度计算 在利用计算机进行数值处理时,有时会遇到这样的问题:有些计算要求精度高,希望计算的数的位数可达几十位甚至几百位,虽然计算机的计算机精度也算较高了,但因受到硬件的限制,往往达不到实际问题所要求的精度。我们可以利用程序设计的方法去实现这样的高精度计算。 高精度计算中需要处理好以下几个问题: (1)数据的接收和存储方法 数据的接收和存储:当输入的数很长时,可采用字符串方式输入,这样可输入数位很长的数,利用字符串函数和操作运算,将每一位数取出,存入数组中。另一种方法是直接用循环加数组的方法输入数据。 (2)高精度数位数的确定 接收时往往采用字符串,所以它的位数就等于字符串的长度。 (3)进位、借位处理 加法进位:c[i]:=a[i]+b[i]; if c[i]>=10 then begin c[i]:=c[i] mod 10; c[i+1]:=c[i+1]+1; end; 减法借位:if a[i]<b[i] then begin a[i+1]:=a[i+1]-1; a[i]:=a[i]+10; end; c[i]:=a[i]-b[i]; 乘法进位: c[i+j-1]:=a[i]*b[j]+x+c[i+j-1]; x:=c[i+j-1] mod 10; C[i+j-1]:=c[i+j-1] mod 10; (4)商和余数的求法:视被除数和除数的位数情况进行处理。
例1、高精度加法。输入两个正整数,求它们的和。 算法分析:输入两个数到两个变量中,然后用赋值语句求它们的和并输出。但是,我们知道,在pascal语言中任何数据类型都有一定的表示范围,当两个被加数很大时,上述算法显然不能求出精确解,因此我们需要寻求另一种方法。加法都采用竖式方法。 如果我们用数组a,b分别存储加数和被加数,用数组c存储结果。则上例有a[1]=6,a[2]=5,a[3]=8,b[1]=5,b[2]=5,b[3]=2。c[1]=1,c[2]=1,c[3]=1,c[4]=1,如上图所示。 因此,算法描述如下: procedure add(a,b;var c); //a,b,c都为数组,分别存储被加数、加数、结果 var i,x:integer; begin i:=1; x:=0; //x是进位 while (i<=a数组长度) or (i<=b数组长度) do begin c[i]:=a[i]+b[i]+x; //第i位相加并加上次的进位 x:=c[i] div 10; //向高位进位 c[i]:=c[i] mod 10; //存储第i位的值 i:=i+1; //位置指针变量 end; end;
读入的整数一般用字符串来存储,方法1程序如下:读入的整数一般用字符串来存储,方法1程序如下: program ex1_1; const max=200; var a,b,c:array[1..max] of 0..9; n;string; lena,lenb,lenc,i,x:integer; begin write(‘input augend:’);readln(n); //输入被加数 lena:=length; //被加数放入a数组 for i:=1 to lena do a[lena-i+1]:=ord(n[i])-ord(‘0’); write(‘input addend:’); readln(n); //输入加数 lenb:=length(n); //加数放入数组b for i:=1 to lenb do b[lenb-i+1]:=ord(n[i])-ord(‘0’); i:=1; x:=0; while (i<=lena) or (i<=lenb) do begin c[i]:=a[i]+b[i]+x; //两数相加,然后加前次进位 x:=c[i] div 10; //x是进位 c[i]:=c[i] mod 10; //保存第i位的值 i:=i+1; end; if x>0 then //处理最高进位 begin lenc:=i; c[i]:=x; end else lenc:=i-1; for i:=lenc downto 1 do write(c[i]); //输出结果 writeln; end.
方法2程序如下: program ex1_2; const n=200; var str1,str2:string; a,b:array[1..n] of integer; k,l1,l2,i,j:integer; begin write(‘input a string str1:’); readln(str1); write(‘input b string str2:’); readln(str2); l1:=length(str1); l2:=length(str2); if l1>l2 then j:=l1 else j:=l2; k:=0; {以上为输入和初始化处理} for i:=l1 downto 1 do begin k:=k+1; a[k]:=ord(str1[i])-ord(‘0); end; k:=0; for i:=l2 downto 1 do begin k:=k+1; b[k]:=ord(str2[i])-ord(‘0); end; //将两个加数分别存入A、B数组中 for i:=1 to j do begin a[i]:=a[i]+b[i]; if a[i]>10 then begin a[i]:=a[i]-10; a[i+1]:=a[i+1]+1; end; //处理两数相加时的进位问题 end; if a[j+1]=0 then j:=j-1; for i:=j+1 downto 1 do write(‘相加和:‘,a[i]); //输出相加的和 writeln; end.
例2、高精度减法。输入两个正整数,求它们的差。例2、高精度减法。输入两个正整数,求它们的差。 类似加法,可以用竖式求减法。在做减法运算时,需要注意的是:被减数必须比减数大,同时需要处理借位。 program ex2; const max=200; var a,b,c:array[1..max] of 0..9; n,n1,n2:string; lena,lenb,lenc,i:integer; begin write(‘input minuend:’);readln(n1); //输入被减数 write(‘input subtrahend:’); readln(n2); //输入减数 if (length(n1)<length(n2)) or (length(n1)=length(n2)) and (n1<n2) then begin n:=n1; n1:=n2; n2:=n; //交换处理减数与被减数 write(‘-’); //交换了减数与被减数,结果为负数 end; lena:=length(n1); lenb:=length(n2); for i:=1 to lena do a[lena-i+1]:=ord(n1[i])-ord(‘0’); for i:=1 to lenb do b[lenb-i+1]:=ord(n2[i])-ord(‘0’); i:=1; while i<=lena do begin if a[i]<b[i] then //不够减,那么向高位借1当10 begin a[i]:=a[i]+10; a[i+1]:=a[i+1]-1; end; c[i]:=a[i]-b[i]; //对应位相减 i:=i+1; end; lenc:=i; while (c[lenc]=0) and (lenc>1) do dce(lenc); //最高位的0不输出 for i:=lenc downto 1 do write(c[i]); end.
例3、高精度乘法。输入两个正整数,求它们的积。例3、高精度乘法。输入两个正整数,求它们的积。 算法分析:类似加法,在做乘法运算时,同样也有进位,同时对每一位进行乘法运算时,必须进行错位相加 分析c数组下标的变化规律,可以写出如下关系式:ci=ci’+ci’’+……由此可见,ci跟a[i]*b[j]乘积有关,跟上次的进位有关,还跟ci的值有关,分析下标规律, 有c[i+j-1]:=a[i]*b[j]+x+c[i+j-1]; x:=c[i+j-1] div 10; c[i+j-1] :=c[i+j-1] mod 10。 如上例:i=1,j=1时,c[1+1-1]=a[1]*b[1]+x+c[1+1-1] (1)初始化时,x=0,c[1+1-1]为空,所以c[1+1-1]=c[1]=6*5=30 (2)x=c[1] div 10=30 div 10=3 (3)c[1]=c[1] mod 10= 30 mod 10=0 当i=2,j=1时,c[2+1-1]=a[2]*b[1]+x+c[2+1-1] (1)c[2]=5*5+3+0=28 (2)x=c[2] div 10=2 (3)c[2]=c[2] mod 10=8 ……
program ex3; const m=200; var a,b,c:array[1..max] of 0..9; n1,n2:string; lena,lenb,lenc,i,j,x:integer; begin write(‘input multiplier:’); readln(n1); write(‘input multiplicand:’); readln(n2) lena:=length(n1); lenb:=length(n2); for i:=1 to lena do a[lena-i+1]:=ord(n1[i])-ord(‘0’); for i:=1 to lenb do b[lenb-i+1]:=ord(n2[i])-ord(‘0’); for i:=1 to lena do begin x:=0; //用于存放数组进位 for j:=1 to lenb do //对乘数的每一位进行处理 begin c[i+j-1]:=a[i]*b[j]+x+c[i+j-1]; //当前乘积+上次乘各进位+原数 x:=c[i+j-1] div 10; c[i+j-1]:=c[i+j-1] mod 10; end; c[i+j]:=x; //进位 lenc:=lena+lenb; while (c[lenc]=0) and (lenc>1) do dce(lenc); for i:=lenc downto 1 do write(c[i]); wirteln; end
例4、高精度除法。输入两个正整数,求它们的商(做整除)。 算法分析:做除法时,每一次商的值都在0----9,每次求得的余数连接以后的若干位得到新的被除数,继续做除法。因此,在做高精度除法时,要涉及到乘法运算和减法运算,还有移位处理。如:123456789÷45=1’2345’6789÷45=274’3484 因为 1 div 45=0 1 mod 45=1 所以取12345 div 45=274 因为 12345 mod 45=15 所以取 156789 div 45=3484 所以答案为 2743484,余数为156789 mod 45=9 方法1:program ex4; const max=200; var a,c:array[1..max] of 0..9; x,b:longint; n1,n2:string; lena,code,i,j:integer; begin write(‘input dividend:’); readln(n1); write(‘input divisor:’); readln(n2); lena:=length(n1); for i:=1 to lena do a[i]:=ord(n1[i])-ord(‘0); val(n2,b,code); //字符串n2转换成数值b,code参数可省略 x:=0; for i:=1 to lena do //按位相除 begin c[i]:=(x*10+a[i]) div b; x:=(x*10+a[i]) mod b; end; j:=1; while (c[j]=0) and (j<lena) do inc(j); //去除高位的0 for i:=j to lena do write(c[i]); end.
方法2:由数学知识可知:除法运算中被除数、除数和商、余数的关系是:方法2:由数学知识可知:除法运算中被除数、除数和商、余数的关系是: 新的被除数=10*余数;商=trunc(被除数/除数);余数=被除数 mod 除数。 根据这关系,程序如下: program ex4_2; const e=500; var a,d,x:array[0..e] of integer; n,m,t:integer; begin write(‘input n,m:’); readln(n,m); write(n,’/’,m,’=‘); a[0]:=n;d[0]:=n div m; x[0]:=n mod m; write(d[o],’.’); //得到整数部分的商 for t:=1 to e do begin] if x[t-1]=0 then exit; a[t]:=x[t-1]*10; //将余数扩大10倍 d[t]:=a[t] div m; write(d[t]); //计算商 x[t]:=a[t] mod m; //生成余数; end; end. 输入为 input n,m:355 113 输出为 355/113=3.14159290353982...... 小数点后有500位的数据
练习题: • 1、求n!的值,利用过程缩精度。 • 问题描述:用高精度方法,求n!的精确值(n以一般整数输入) • 输入样例:ni.in • 10 • 输出样例:ni.out • 362880 • 2、求a/b的高精度值 • 问题描述:计算a/b的精确值,设a,b以一般整数输入,计算结果精确到小数后20位。 • 样例输入:ab,in • 3 • 样例输出:ab,out • 4/3=1.333333333333333333 • 6/5=1.2 • 3、求n累加和 • 问题描述:用高精度方法,求s=1+2+3+…n的精确值(n以一般整数输入). • 输入样例:ja.in • 10 • 输出样例:ja.out • 55
4、阶段和 问题描述:已知正整数n(n<=100),设s=1!+2!+3!+…+n! 请编程实现,输入正整数n,输出计算结果s的值。 输入样例:sum.in 4 输出样例:sum.out 33 5、高精度求积(multiply.pas) 问题描述:输入两个高精度正整数m和n(m和n均小于100位),求这两个高精度的积。 输入样例:multiply.in 36 3 输出样例:multiply.out 108