310 likes | 454 Views
高精度运算. 类型 数值范围 占字节数 Byte 0 .. 255 1 Word 0..65535 2 Shortint -128 .. 127 1
E N D
类型数值范围占字节数 Byte 0 .. 255 1 Word 0..65535 2 Shortint -128 .. 127 1 Integer -32768..32767; 2 Longint -2147483648 .. 2147483647 4 Longword 0..4294967295 4 Int64 -9223372036854775808 .. 9223372036854775807 8 QWord 0 .. 18446744073709551615 8
n!=1×2×3×...×(n-1)×n (N>0) 0!=1 若用integer型数据表示阶乘,最多可到7!,用Longint类型也只能到12! 要求输入正整数n,求 n! 的精确表示 【算法分析】用数组存放结果,模拟人工计算过程,逐位去乘,注意进位情况的处理。
所谓的高精度运算,是指参与运算的数(加数,减数,因子……)范围大大超出了标准数据类型(整型,实型)能表示的范围的运算。例如,求两个200位的数的和。这时,就要用到高精度算法了
高精度运算主要解决以下三个问题 1、加数、减数、运算结果的输入和存储 运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。在Pascal中,能表示多个数的数 据类型有两种:数组和字符串。 (1)数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素; 用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便 用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯; (2)字符串:字符串的最大长度是255,可以表示255位。 用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯; 用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便
高精度运算涉及到的问题: 1、数据的输入。 2、数据的存储。 3、数据的运算:进位和借位。 4、结果的输出:小数点的位置、处理多于的0等。
一、高精度运算:加法 题目要求: 输入: 第一行:正整数a。 第二行:正整数b。已知:a和b(<10240)。 输出:a+b的值。 样例输入: 99 999 样例输出: 1098
高精度加法解决的问题: 1、数据的输入。 2、数据的存储。 3、加法运算,注意进位处理。 4、结果的输出。
1、数据的输入。 a和b(<10240) 字符串输入: Var s1,s2:string; Readln(s1); Readln(s2);
2、数据的存储。 为了计算方便,采用数组存储。 Var a,b:array[1..240] of integer; 将字符串转换为数组存储。 用a存s1,b存s2。 A[1]存个位,便于以后计算和进位处理 S1=’3 4 5 2 3 4 5’ ….a[3] a[2] a[1] len1:=length(s1); for i:= 1 to len1 do a[i]:=ord(s1[len1+1-i])-48; len2:=length(s2); for i:= 1 to len2 do b[i]:=ord(s2[len2+1-i])-48;
3、加法运算,注意进位处理。 把计算结果存到数组c中:c:array[1..241] of integer; 先计算。 …….a[3] a[2] a[1] ……b[3] b[2] b[1] + ……c[3] c[2] c[1] if len1>len2 then len:=len1 else len:=len2; for i:=1 to len do c[i]:=a[i]+b[i];{直接先计算} 计算后的c[i]可能>=10,怎样处理?
处理进位: for i:=1 to len do begin c[i+1]:=c[i+1]+c[i] div 10; c[i]:=c[i] mod 10; end; 4、结果的输出:数组c。 if c[len+1]>0 then len:=len+1; for i:=len downto 1 do write(c[i]);
const maxn=240; {c=a+b,先加,然后再处理进位} var s1,s2:string; a,b:array[1..maxn] of integer; c:array[1..maxn+1] of integer; len,len1,len2,k,j,i:integer; begin readln(s1); {输入} readln(s2); len1:=length(s1); {数据的保存} for i:= 1 to len1 do a[i]:=ord(s1[len1+1-i])-48; len2:=length(s2); for i:= 1 to len2 do b[i]:=ord(s2[len2+1-i])-48; if len1>len2 then len:=len1 else len:=len2; for i:=1 to len do c[i]:=a[i]+b[i]; for i:=1 to len do begin c[i+1]:=c[i+1]+c[i] div 10; c[i]:=c[i] mod 10; end; if c[len+1]>0 then len:=len+1; for i:=len downto 1 do write(c[i]); end.
算法改进一:边计算边处理进位。 {c=a+b,边加边处理进位} const maxn=240; var s1,s2:string; a,b:array[1..maxn] of integer; c:array[1..maxn+1] of integer; len,len1,len2,k,j,i:integer; begin readln(s1); readln(s2); len1:=length(s1); for i:= 1 to len1 do a[i]:=ord(s1[len1+1-i])-48; len2:=length(s2); for i:= 1 to len2 do b[i]:=ord(s2[len2+1-i])-48; if len1>len2 then len:=len1 else len:=len2; for i:=1 to len do begin c[i+1]:=(a[i]+b[i]+c[i]) div 10; c[i]:=(a[i]+b[i]+c[i]) mod 10; end; if c[len+1]>0 then len:=len+1; for i:=len downto 1 do write(c[i]); end.
算法改进二:结果存在数组a中,不再定义多余的数组c,减少存储空间。(最好的方法:a=a+b)算法改进二:结果存在数组a中,不再定义多余的数组c,减少存储空间。(最好的方法:a=a+b) begin readln(s1); readln(s2); len1:=length(s1); len2:=length(s2); fillchar(a,sizeof(a),0); fillchar(b,sizeof(b),0); for i:=1 to len1 do a[i]:=ord(s1[len1-i+1])-48; for i:=1 to len2 do b[i]:=ord(s2[len2-i+1])-48; if len1>len2 then len:=len1 else len:=len2; for i:=1 to len do begin a[i+1]:=a[i+1]+(a[i]+b[i]) div 10; a[i]:=(a[i]+b[i]) mod 10; end; if a[len+1]>0 then len:=len+1; for i:=len downto 1 do write(a[i]); writeln; end.
高精度加法的应用 Fibonacci数列 Fibonacci数列的代表问题是由意大利著名数学家Fibonacci于1202年提出的“兔子繁殖问题”(又称“Fibonacci问题”)。 问题的提出:有雌雄一对兔子,假定过两个月后便每个月可繁殖雌雄各一的一对小兔子。问过n个月后共有多少对兔子? 已知:N<=93。 F(i):第i个月后共有的兔子对数。 F(1)=1; F(2)=1; f(3)=2; f(4)=3; f(5)=5; f(6)=8; F(i)=f(i-2)+f(i-1)
{N<=93} var f:array[1..100] of qword; var n,i:integer; begin readln(n); f[1]:=1; f[2]:=1; for i:=3 to n do f[i]:=f[i-2]+f[i-1]; writeln(f[n]); end.
for k:=3 to n do begin fillchar(f,sizeof(f),0); for i:=1 to len do begin f[i+1]:=(f1[i]+f2[i]+f[i]) div 10; f[i]:=(f1[i]+f2[i]+f[i]) mod 10; end; if f[len+1]>0 then len:=len+1; f1:=f2; f2:=f; end; writeln(len); for i:=len downto 1 do write(f[i]); end. N<=1000} var n,len,i,k:integer; f1,f2,f:array[1..210] of integer; begin fillchar(f1,sizeof(f1),0); fillchar(f2,sizeof(f2),0); readln(n); f1[1]:=1; F2[1]:=1; len:=1;
二、高精度减法运算 问题表述: 输入a,b(<10240)两个数,输出a-b的值。 样例2输入: 999 1000 样例2输出: -1 样例1输入: 456 409 样例1输出: 47
算法: 1、读入被减数s1; 2、读入减数s2; 3、如果s1<s2 ,那么交换s1和s2。{只需要计算s1-s2} 4、把s1存到数组a; 5、把s2存到数组b; 6、从低到高位计算a[i]=a[i]-b[i];注意借位。 7、输出数组a就是运算结果。
解决的问题: 1、输入、保存。 2、比较a和b的大小。从而确定结果的正负号 3、借位问题。 4、输出。
1、比较大小:如果s1>s2,不交换,如果s1<s2,交换s1和s2,保证后边s1-s2。1、比较大小:如果s1>s2,不交换,如果s1<s2,交换s1和s2,保证后边s1-s2。 S1<s2的条件: if (length(s1)<length(s2))or ((length(s1)=length(s2))and(s1<s2)) then begin fh:='-'; s:=s1; s1:=s2; s2:=s; end; 然后数组a存s1,b存s2。计算a=a-b
2、借位: if a[i]<b[i] then begin a[i+1]:=a[i+1]-1; a[i]:=a[i]+10; end; a[i]:=a[i]-b[i];
{a=a-b} type numtype=array[1..240] of integer; var a,b:numtype; s1,s2,s:string; la,lb,k:integer; i:integer; fh:char; begin readln(s1); readln(s2); if s1=s2 then begin writeln(0);halt;end; if (length(s1)<length(s2))or ((length(s1)=length(s2))and(s1<s2)) then begin fh:='-';s:=s1;s1:=s2;s2:=s;end; la:=length(s1); lb:=length(s2); fillchar(a,sizeof(a),0); fillchar(b,sizeof(b),0);
for i:=1 to la do a[i]:=ord(s1[la-i+1])-48; for i:=1 to lb do b[i]:=ord(s2[lb-i+1])-48; k:=la; for i:=1 to k do begin if a[i]<b[i] then begin a[i+1]:=a[i+1]-1; a[i]:=a[i]+10; end; a[i]:=a[i]-b[i]; end; while a[k]=0 do k:=k-1; if fh='-' then write(fh); for i:=k downto 1 do write(a[i]); writeln; END.
for i:=1 to k do begin if a[i]<b[i] then begin a[i+1]:=a[i+1]-1; a[i]:=a[i]+10; end; a[i]:=a[i]-b[i]; end; while a[k]=0 do k:=k-1; for i:=k downto 1 do write(a[i]); end; BEGIN readln(s1); readln(s2); if s1=s2 then begin writeln(0);halt;end; if (length(s1)>length(s2))or ((length(s1)=length(s2))and(s1>s2)) then sub(s1,s2) else begin write('-');sub(s2,s1);end; END. {a=a-b} var a,b:array[1..240] of integer; s1,s2:string; procedure sub(x,y:string); var la,lb,k:integer; i:integer; begin la:=length(x); lb:=length(y); fillchar(a,sizeof(a),0); fillchar(b,sizeof(b),0); for i:=1 to la do a[i]:=ord(x[la-i+1])-48; for i:=1 to lb do b[i]:=ord(y[lb-i+1])-48; k:=la;
上机出现的问题: 1、数组的初始化: 数组使用前一般都要初始化: A:array[1..100,1..100] of integer; for i:=1 to 100 do for j:=1 to 100 do a[i,j]:=0; Fillchar(a,sizeof(a),0); 2、变量的使用要规范: 如:循环控制变量一般用 i 、 j 、 k,一般不要用m、n等。这样容易找错误。 3、如果求a+b,(a,b<=102000) 定义:a,b:ansistring;{长度最多2552=65025}
? if a[len+1]>0 then inc(len); 三、高精度乘法 1、高精度乘单精度(整数),求a*b,a<=10200,b<=108 var i,len:integer; s:string; b,m:longint; a:array[1..250] of longint; begin readln(s); {读入被乘数a} readln(b); {读入乘数b} len:=length(s); for i:=1 to len do a[i]:=ord(s[len-i+1])-48; for i:=1 to len do {每项乘b} a[i]:=a[i]*b; for i:=1 to len do {处理进位} begin a[i+1]:=a[i+1]+a[i] div 10; a[i]:=a[i] mod 10; end; for i:=len downto 1 do write(a[i]); end.{multiply} m:=a[len+1];{处理最高进位} while m<>0 do begin {处理最高位的进位} inc(len); a[len]:=m mod 10; m:=m div 10; end;
2、高精度乘高精度 var i,j,la,lb,len:integer; s1,s2:string; m:longint; a,b,c:array[1..250] of integer; begin readln(s1); la:=length(s1); for i:=1 to la do a[i]:=ord(s1[la-i+1])-48; readln(s2); lb:=length(s2); for i:=1 to lb do b[i]:=ord(s2[lb-i+1])-48; for i:=1 to la do for j:=1 to lb do c[i+j-1]:=c[i+j-1]+a[i]*b[j]; len:=la+lb; for i:=1 to len do begin c[i+1]:=c[i+1]+c[i] div 10; c[i]:=c[i] mod 10; end; while c[len]=0 do dec(len); m:=c[len]; while m>0 do begin c[len]:=m mod 10;m:=m div 10;inc(len);end; for i:=len-1 downto 1 do write(c[i
四、高精度除法 {高精度a除以单精度b} type data=array[0..300] of integer; var a,c:data; s1,s2:string; len,k:integer; i:integer; b,d:longint; procedure init; begin readln(s1); readln(b); fillchar(a,sizeof(a),0); len:=length(s1); for i:=1 to len do a[i]:=ord(s1[len-i+1])-48; end; procedure devide; {c:=a div b; d:= a mod b} var i:integer; begin fillchar(c,sizeof(c),0); d:=0; for i:=len downto 1 do begin d:=d*10+a[i]; c[i]:=d div b; d:=d mod b; end; while (len>=1) and (c[len]=0) do dec(len); for i:=len downto 2 do write(c[i]); writeln(c[1]); {writeln(d);} end; Begin init; devide; end.
上机练习: 1、求n!=1*2*3*。。。*n (n<=100) 2、求1!+2!+3!+。。。+n! (n<=100) 3、输入两个正的实数a,b,输出a+b的值。 输入:两行,第一行a,第二行b,a和b的长度均小于1000位。 输出:一行,a+b的值。 样例: 输入: 4.5 2.3 输出: 6.8