可以用一个字符串string读入,再转换成单个数字存在数组中。 如果位数超过了225位,就用长字符串asistring读入。 一般为: c,d:string; a,b:array[1..500]of integer; …… readln(c); readln(d); len:=length(c); for i:=1 to len do a[i]:=ord(c[len-i+1])-ord('0');
夏了夏天
2024-12-02 00:16:38
所谓的高精度运算,是指参与运算的数(加数,减数,因子……)范围大大超出了标准数据类型(整型,实型)能表示的范围的运算。例如,求两个200位的数的和。这时,就要用到高精度算法了。在这里,我们先讨论高精度加法。高精度运算主要解决以下三个问题: 一、加数、减数、运算结果的输入和存储 运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。在Pascal中,能表示多个数的数据类型有两种:数组和字符串。 数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便。用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯; 字符串:字符串的最大长度是255,可以表示255位。用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便; 综合以上所述,对上面两种数据结构取长补短:用字符串读入数据,用数组存储数据: var s1,s2:string; a,b,c:array [1..260] of integer; i,l,k1,k2:integer; begin write('input s1:');readln(s1); write('input s2:');readln(s2); {————读入两个数s1,s2,都是字符串类型} l:=length(s1);{求出s1的长度,也即s1的位数;有关字符串的知识。} k1:=260; for i:=l downto 1 do begin a[k1]:=ord(s1)-48;{将字符转成数值} k1:=k1-1; end; k1:=k1+1; {————以上将s1中的字符一位一位地转成数值并存在数组a中;低位在后(从第260位开始),高位在前(每存完一位,k1减1),完后,k1指向最高位} 对s2的转化过程和上面一模一样。 二、运算过程 在往下看之前,大家先列竖式计算35+86。 注意的问题: (1)运算顺序:两个数靠右对齐;从低位向高位运算;先计算低位再计算高位; (2)运算规则:同一位的两个数相加再加上从低位来的进位,成为该位的和;这个和去掉向高位的进位就成为该位的值;如上例:3+8+1=12,向前一位进1,本位的值是2;可借助MOD、DIV运算完成这一步; (3)最后一位的进位:如果完成两个数的相加后,进位位值不为0,则应添加一位; (4)如果两个加数位数不一样多,则按位数多的一个进行计算; if k1>k2 then k:=k1 else k:=k2; y:=0; for i:=260 downto k do begin x:=a+b+y; c:=x mod 10; y:=x div 10; end; if y<>0 then begin k:=k-1;c[k]:=y; end; 三、结果的输出(这也是优化的一个重点) 按运算结果的实际位数输出 for i:=k to 260 do write(c); writeln; 例子:求两个数的加法 program sum; var s,s1,s2:string; a,b,c:array [1..260] of integer; i,l,k1,k2:integer; begin write('input s1:');readln(s1); write('input s2:');readln(s2); l:=length(s1); k1:=260; for i:=l downto 1 do begin a[k1]:=ord(s1)-48; k1:=k1-1; end; k1:=k1+1; l:=length(s2); k2:=260; for I+:=l downto 1 do begin b[k2]:=ord(s2)-48; k2:=k2-1; end; k2:=k2+1; if k1>k2 then k:=k2 else k:=k1; y:=0; for i:=260 downto k do begin x:=a+b+y; c:=x mod 10; y:=x div 10; end; if y<>0 then begin k:=k-1;c[k]:=y; end; for i:=k to 260 do write(c); writeln; end. 四、优化: 以上的方法的有明显的缺点: (1)浪费空间:一个整型变量(-32768~32767)只存放一位(0~9); (2)浪费时间:一次加减只处理一位; 针对以上问题,我们做如下优化:一个数组元素存放四位数;(integer的最大范围是32767,5位的话可能导致出界)。具体方法: l:=length(s1); k1:=260; repeat {————有关字符串的知识} s:=copy(s1,l-3,4); val(s,a[k1],code); k1:=k1-1; s1:=copy(s1,1,l-4); l:=l-4; until l<=0; k1:=k1+1; 而因为这个改进,算法要相应改变: (1)运算时:不再逢十进位,而是逢万进位(mod 10000; div 10000); (2)输出时:最高位直接输出,其余各位,要判断是否足够4位,不足部分要补0;例如:1,23,2345这样三段的数,输出时,应该是100232345而不是1234567。 改进后的算法: program sum; var s1,s2,s:string; a,b,c:array [1..260] of integer; i,l,k1,k2,code:integer; begin write('input s1:');readln(s1); write('input s2:');readln(s2); l:=length(s1); k1:=260; repeat {————有关字符串的知识} s:=copy(s1,l-3,4); val(s,a[k1],code); k1:=k1-1; s1:=copy(s1,1,l-4); l:=l-4; until l<=0; k1:=k1+1; l:=length(s2); k2:=260; repeat s:=copy(s2,l-3,4); val(s,b[k2],code); k2:=k2-1; s2:=copy(s2,1,l-4); l:=l-4; until l<=0; k2:=k2+1; if k1<k2 then k:=k1 else k:=k2; y:=0; for i:=260 downto k do begin x:=a+b+y; c:=x mod 10000; y:=x div 10000; end; if y<>0 then begin k:=k-1;c[k]:=y;end; write(c[k]); for i:=k+1 to 260 do begin if c<1000 then write('0'); if c<100 then write('0'); if c<10 then write('0'); write(c); end; writeln; end. 减法:和高精度加法相比,减法在差为负数时处理的细节更多一点:当被减数小于减数时,差为负数,差的绝对值是减数减去被减数;在程序实现上用一个变量来存储符号位,用另一个数组存差的绝对值。 2、算法流程: (1)读入被减数S1,S2(字符串); (2)置符号位:判断被减数是否大于减数:大则将符号位置为空;小则将符号位置为“-”,交换减数与被减数; (3)被减数与减数处理成数值,放在数组中; (4)运算: A、取数; B、判断是否需要借位; C、减,将运算结果放到差数组相应位中; D、判断是否运算完成:是,转5;不是,转A; (5)打印结果:符号位,第1位,循环处理第2到最后一位; 3、细节: ▲如何判断被减数与减数的大小:字符串知识 (1)首先将两个字符串的位数补成一样(因为字符串的比较是从左边对齐的;两个字符串一样长才能真正地比较出大小):短的在左边补0 k1:=length(s1); k2:=length(s2); if k1>k2 then for i:=1 to k1-k2 do s2:='0'+s2 else for i:=1 to k2-k1 do s1:='0'+s1; (2)接着比较大小:直接比较字符串大小 fh:=''; if s1<s2 then begin fh:='-';s:=s1; s1:=s2; s2:=s; end; {————s1存被减数,fh存符号} ▲将字符串处理成数值: l:=length(s1);{求出s1的长度,也即s1的位数;有关字符串的知识。} k1:=260; for i:=l downto 1 do begin a[k1]:=ord(s1)-48;{将字符转成数值} k1:=k1-1; end; k1:=k1+1; ▲运算(减法跟加法比较,减法退位处理跟加法进位处理不一样): a.处理退位; 跟加法一样,在for语句外面先将退位清零, 用被减数再减去退位, {注意:由于每一个数位不一定都得向前一位借位,所以这里退位得清零。例如,234-25,个位需借位,而十位不用} 接着,再判断,当被减数某一位不够减时,则需加上前一位退位过来的数。 {注意:由于这里采用优化方法,所以退一位,就等于后一位加上10000。) 最后,再拿一个数组来存储两个减数的差。 jw:=0; for i:=260 downto k1 do begin a:=a-jw; {此处jw为下一位从I位的借位} jw:=0; {此处jw为I 位准备向上一位的借位} if a<b then begin jw:=1; a:=a+10000; end; c:=a-b; end; ▲打印结果: 先找到差的第一个非零数,如果差的所有位数都为零,就直接输出零。 如果不是,就输出符号位和差的第一位。 剩下部分,打印补足零: 因为优化后的高精度减法,是把每四个数位分成一段,而每一段则必须有四个 数,当有一段不足四个数时,就得用"0"补足.(如:第一位是'1',第二位是'34',第三位是'345',第四位是'8', 则应写为'1003403450008').注意:第一位不用补零,(如:第一位为'3',则写成'3'). while (c[k]=0) and (k<=260) do k:=k+1; if k>260 then write('0') else begin write(fh,c[k]);{k是差的第1位;} for i:=k+1 to 260 do begin if c<1000 then write('0'); if c<100 then write('0'); if c<10 then write('0'); write(c); end; end; 参考程序: program Zfjianfa; const n=25; var s1,s2,s3,s4,s:string; a,b,c:array[1..n] of integer; i,k1,k2,l,code,jw:integer; cq:char; begin readln(s1); readln(s2); k1:=length(s1); k2:=length(s2); if k1>k2 then for i:=1 to k1-k2 do s2:='0'+s2 else for i:=1 to k2-k1 do s1:='0'+s1; cq:=' '; if s1<s2 then begin cq:='-'; s:=s1; s1:=s2; s2:=s; end; l:=length(s1); k1:=n; repeat s3:=copy(s1,l-3,4); val(s3,a[k1],code); k1:=k1-1; delete(s1,l-3,4); l:=l-4; until l<=0; k1:=k1+1; i:=length(s2); k2:=n; repeat s4:=copy(s2,i-3,4); val(s4,b[k2],code); k2:=k2-1; delete(s2,i-3,4); i:=i-4; until i<=0; k2:=k2+1; jw:=0; for i:=n downto k1 do begin a:=a-jw; jw:=0; if a<b then begin jw:=1; a:=a+10000; end; c:=a-b; end; while (c[k1]=0) and (k1<=n) do k1:=k1+1; if k1>n then writeln('0') else begin write(cq,c[k1]); for i:=k1+1 to n do begin if c<1000 then write('0'); if c<100 then write('0'); if c<10 then write('0'); write(c); end; end; writeln; end. 高精度乘法基本思想和加法一样。其基本流程如下: ①读入被乘数s1,乘数s2 ②把s1、s2分成4位一段,转成数值存在数组a,b中;记下a,b的长度k1,k2; ③i赋为b中的最低位; ④从b中取出第i位与a相乘,累加到另一数组c中;(注意:累加时错开的位数应是多少位?) ⑤i:=i-1;检测i值:小于k2则转⑥,否则转④ ⑥打印结果 参考程序: program chengfa; const n=100; type ar=array [1..n] of integer; var a,b:ar; k1,k2,k:integer; c:array [1..200] of integer; s1,s2:string; procedure fenge(s:string;var d:ar; var kk:integer); {将s分割成四位一组存放在d中,返回的kk值指向d的最高位} var ss:string; i,code:integer; begin i:=length(s); kk:=n; repeat ss:=copy(s,i-3,4); val(ss,d[kk],code); kk:=kk-1; s:=copy(s,1,i-4); i:=i-4; until i<0; kk:=kk+1; end; procedure init; var i:integer; begin for i:=1 to n do begin a:=0; b:=0; end; for i:=1 to 2*n do c:=0; write('input 2 numbers:'); readln(s1); readln(s2); fenge(s1,a,k1); fenge(s2,b,k2); end; procedure jisuan; var i,j,m:integer; x,y,z,jw:longint; begin i:=n; k:=2*n; repeat x:=b; z:=0; m:=k; jw:=0; for j:=n downto k1 do begin y:=a[j]; z:=c[m]; x:=x*y+z+jw; jw:=x div 10000; c[m]:=x mod 10000; m:=m-1; x:=b; end; if jw<>0 then c[m]:=jw else m:=m+1; i:=i-1; k:=k-1; until i<k2; k:=m; end; procedure daying; var i:integer; begin write(c[k]); for i:=k+1 to 2*n do begin if c<1000 then write('0'); if c<100 then write('0'); if c<10 then write('0'); write(c); end; writeln; end; begin init; jisuan; daying; end. 教材“基础编”P87高精乘法参考程序: program ex3_1; var a,b,c:array[0..1000] of word; procedure init; var s:string; ok,i,j:integer; begin readln(s); a[0]:=length(s); for i:=1 to a[0] do val(s[a[0]-i+1],a,ok); readln(s); b[0]:=length(s); b[0]:=length(s); for i:=1 to b[0] do val(s[b[0]-i+1],b,ok); end; procedure highmul; var i,j,k:integer; begin c[0]:=a[0]+b[0]; for i:=1 to b[0] do for j:=1 to a[0]+1 do begin inc(c[i+j-1],a[j]*b mod 10); c[i+j]:=c[i+j]+(a[j]*b div 10)+(c[i+j-1] div 10); c[i+j-1]:=c[i+j-1] mod 10; end; end; procedure print; var i:integer; begin while c[c[0]]=0 do dec(c[0]); for i:=c[0] downto 1 do write(c); writeln; end; begin init; highmul; print; end. 高精度除法: 1).高精度除以整型数据(integer); 程序如下: program HighPrecision3_Multiply1; const fn_inp='hp5.inp'; fn_out='hp5.out'; maxlen=100; { max length of the number } type hp=record len:integer; { length of the number } s:array[1..maxlen] of integer { s[1] is the lowest position s[len] is the highest position } end; var x,y:hp; z,w:integer; procedure PrintHP(const p:hp); var i:integer; begin for i:=p.len downto 1 do write(p.s); end; procedure init; var st:string; i:integer; begin assign(input,fn_inp); reset(input); readln(st); x.len:=length(st); for i:=1 to x.len do { change string to HP } x.s:=ord(st[x.len+1-i])-ord('0'); readln(z); close(input); end; procedure Divide(a:hp;b:integer;var c:hp;var d:integer); { c:=a div b ; d:=a mod b } var i,len:integer; begin fillchar(c,sizeof(c),0); len:=a.len; d:=0; for i:=len downto 1 do { from high to low } begin d:=d*10+a.s; c.s:=d div b; d:=d mod b; end; while(len>1) and (c.s[len]=0) do dec(len); c.len:=len; end; procedure main; begin Divide(x,z,y,w); end; procedure out; begin assign(output,fn_out); rewrite(output); PrintHP(y); writeln; writeln(w); close(output); end; begin init; main; out; end. 2).高精度除以高精度 程序如下: program HighPrecision4_Multiply2; const fn_inp='hp6.inp'; fn_out='hp6.out'; maxlen=100; { max length of the number } type hp=record len:integer; { length of the number } s:array[1..maxlen] of integer { s[1] is the lowest position s[len] is the highest position } end; var x:array[1..2] of hp; y,w:hp; { x:input ; y:output } procedure PrintHP(const p:hp); var i:integer; begin for i:=p.len downto 1 do write(p.s); end; procedure init; var st:string; j,i:integer; begin assign(input,fn_inp); reset(input); for j:=1 to 2 do begin readln(st); x[j].len:=length(st); for i:=1 to x[j].len do { change string to HP } x[j].s:=ord(st[x[j].len+1-i])-ord('0'); end; close(input); end; procedure Subtract(a,b:hp;var c:hp); { c:=a-b, suppose a>=b } var i,len:integer; begin fillchar(c,sizeof(c),0); if a.len>b.len then len:=a.len { get the bigger length of a,b } else len:=b.len; for i:=1 to len do { subtract from low to high } begin inc(c.s,a.s-b.s); if c.s<0 then begin inc(c.s,10); dec(c.s[i+1]); { add 1 to a higher position } end; end; while(len>1) and (c.s[len]=0) do dec(len); c.len:=len; end; function Compare(const a,b:hp):integer; { 1 if a>b 0 if a=b -1 if a < b } var len:integer; begin if a.len>b.len then len:=a.len { get the bigger length of a,b } else len:=b.len; while(len>0) and (a.s[len]=b.s[len]) do dec(len); { find a position which have a different digit } if len=0 then compare:=0 { no difference } else compare:=a.s[len]-b.s[len]; end; procedure Multiply10(var a:hp); { a:=a*10 } var i:Integer; begin for i:=a.len downto 1 do a.s[i+1]:=a.s; a.s[1]:=0; inc(a.len); while(a.len>1) and (a.s[a.len]=0) do dec(a.len); end; procedure Divide(a,b:hp;var c,d:hp); { c:=a div b ; d:=a mod b } var i,j,len:integer; begin fillchar(c,sizeof(c),0); len:=a.len; fillchar(d,sizeof(d),0); d.len:=1; for i:=len downto 1 do begin Multiply10(d); d.s[1]:=a.s; { d:=d*10+a.s } { c.s:=d div b ; d:=d mod b; } { while(d>=b) do begin d:=d-b;inc(c.s) end } while(compare(d,b)>=0) do begin Subtract(d,b,d); inc(c.s); end; end; while(len>1)and(c.s[len]=0) do dec(len); c.len:=len; end; procedure main; begin Divide(x[1],x[2],y,w); end; procedure out; begin assign(output,fn_out); rewrite(output); PrintHP(y); writeln; PrintHP(w); writeln; close(output); end; begin init; main; out; end.