[b]高精度1[/b]
[b]高精度2[/b]
[b]高精度3[/b]
[b]高精度4[/b]
基本方法
1、加数、减数、运算结果的输入和存储
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。在Pascal中,能表示多个数的数据类型有两种:数组和字符串。
(1)数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;
用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便
用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯;
(2)字符串:字符串的最大长度是255,可以表示255位。
用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;
用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便;
(3)因此,综合以上所述,对上面两种数据结构取长补短:用字符串读入数据,用数组存储数据:
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)}
对s2的转化过程和上面一模一样。
2、运算过程
在往下看之前,大家先列竖式计算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;
3、结果的输出(这也是优化的一个重点)
按运算结果的实际位数输出
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: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.
高精度减法
1、和高精度加法相比,减法在差为负数时处理的细节更多一点:当被减数小于减数时,差为负数,差的绝对值是减数减去被减数;在程序实现上用一个变量来存储符号位,用另一个数组存差的绝对值。
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存被减数,符号存符号}
▲将字符串处理成数值:
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;
▲打印结果:
例:差:第一位是12,第二位是234,第三位是1234,最后一位:3。它的实际数值是12023412340003。
从上例可以看出:打印时,从第二位开始,因为每一段都代表4位,不足4位的要补足0。
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;
高精度乘法和阶乘
一、高精度乘法基本思想和加法一样。其基本流程如下:
①读入被乘数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);
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 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.
二、求N!当N稍微大一点时结果就很大了(如n=10时N!=3628800;n=20时N!=2432902008176640000);因此,求阶乘通常都要用到高精度算法。基本过程和乘法一样,只是多了控制乘法次数的一层循环。
var
a,b,c:array[1..1000] of integer;
i,j,l,m,k1,k2,x,y,z,w,n,t:integer;
procedure chengfa; {高精度乘法}
begin
for l:=1 to k1 do
for m:=1 to k2 do
begin
x:=a[l]*b[m];
y:=x div 10;
z:=x mod 10;
w:=l+m-1;
c[w]:=c[w]+z;
c[w+1]:=c[w+1]+y+c[w] div 10;
c[w]:=c[w]mod 10
end;
k1:=k1+k2; {位数为K1、K2的两数相乘最大只有K1+K2位}
if c[k1]=0 then k1:=k1-1; {如果最高位为0则位数减少一位}
for t:=1 to k1 do a[t]:=c[t]; {把一次高精度相乘的结果放到A数组中,以便下次相乘}
for t:=1 to k1 do c[t]:=0; {同时把数组C清空,以便下次相乘,因为每调用此过程一次都是一次 全新的高精度乘法,所以数组C必须清空}
end;
procedure zhuanhuan; {把I每一位分解开分别赋值给数组B的每一个元素}
begin
if i>=100 then
begin
k2:=3;
b[3]:=i div 100;
b[2]:=(i-b[3]*100) div 10;
b[1]:=i-b[3]*100-b[2]*10
end
else
if i>=10
then
begin
k2:=2;
b[2]:=i div 10;
b[1]:=i-b[2]*10
end
else
begin
k2:=1;
b[1]:=i
end;
end;
begin
writeln('input:');
readln(n);
a[1]:=1; {最后结果放在数组A中}
k1:=1;
for i:=2 to n do
begin
zhuanhuan;
chengfa;
end;
writeln(n:2,'!= ');
for i:=k1 downto 1 do write(a)
end.
高精度除法(1)
{
author : tenshi
date : 2002-03-10
problem : high precision --- divide
algorithm : simple
input : two lines,
1st line : a positive highprecision number
2nd line : a positive low precicion number
output : times & rest of the two numbers
}
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)
{
author : tenshi
date : 2002-03-10
problem : high precision --- divide
algorithm : simple
input : two lines, each line a positive highprecision number
output : quotient & modulus of the two highprecision numbers
}
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.
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <set>
#include <queue>
#include <map>
using namespace std;
#define MAX 500
int Radix(char *toStr, char *fromStr)
{
int i = 0, j = 0;
int len;
while (fromStr[i] != '.' && fromStr[i] != '\0')
{
toStr[j++] = fromStr[i++];
}
len = i++;
while (fromStr[i] != '\0')
{
toStr[j++] = fromStr[i++];
}
return i - len - 1;
}
void IntAddition(char *augend, char *addend, char *sum)
{
int cAug[MAX] = {0};
int cAdd[MAX] = {0};
int cSum[MAX] = {0};
int lenAug = strlen(augend), lenAdd = strlen(addend);
int lenMin = lenAug < lenAdd ? lenAug : lenAdd;
int i;
for (i=0; i<lenAug; i++)
cAug[i] = augend[lenAug-1-i] - '0';
for (i=0; i<lenAdd; i++)
cAdd[i] = addend[lenAdd-1-i] - '0';
int carry = 0;
int s = 0;
for (i=0; i<lenMin; i++)
{
s = cAug[i] + cAdd[i] + carry;
cSum[i] = s % 10;
carry = s / 10;
}
while (i < lenAug)
{
s = cAug[i] + carry;
cSum[i] = s % 10;
carry = s / 10;
i++;
}
while (i < lenAdd)
{
s = cAdd[i] + carry;
cSum[i] = s % 10;
carry = s / 10;
i++;
}
if (carry > 0)
cSum[i++] = carry;
int j;
for (j=0; j<i; j++)
sum[j] = cSum[i-1-j] + '0';
sum[i] = '\0';
}
void IntMultiplication(char *multiplicand, char *multiplier, char *product)
{
int cD[MAX] = {0};
int cR[MAX] = {0};
int cP[MAX] = {0};
char tcP[MAX] = "";
int lenD = strlen(multiplicand), lenR = strlen(multiplier);
int i, j, k;
for (i=0; i<lenD; i++)
cD[i] = multiplicand[lenD-1-i] - '0';
for (i=0; i<lenR; i++)
cR[i] = multiplier[lenR-1-i] - '0';
int carry;
int mul = 0;
strcpy(product, "0");
for (i=0; i<lenR; i++)
{
carry = 0;
for (j=0; j<lenD; j++)
{
mul = cD[j] * cR[i] + carry;
cP[j] = mul % 10;
carry = mul / 10;
}
if (carry > 0)
cP[j++] = carry;
while (cP[j-1] == 0)
--j;
for (k=0; k<j; k++)
tcP[k] = cP[j-1-k] + '0';
for (j=0; j<i; j++)
tcP[k++] = '0';
tcP[k] = '\0';
IntAddition(product, tcP, product);
}
}
void FloatMultiplication(char *multiplicand, char *multiplier,char *product)
{
char cD[MAX] = {0};
char cR[MAX] = {0};
char cP[2*MAX] = {0};
int lenD, lenR, lenP;
lenD = Radix(cD, multiplicand);
lenR = Radix(cR, multiplier);
lenP = lenD + lenR;
IntMultiplication(cD, cR, cP);
int i = strlen(cP) - 1;
while (lenP > 0 && cP[i] == '0')
{
i--;
lenP--;
}
cP[i+2] = '\0';
while (lenP > 0 && i >= 0)
{
cP[i+1] = cP[i];
i--;
lenP--;
}
cP[i+1] = '.';
if (i == -1)
{
for (i=strlen(cP); i>0; --i)
cP[i+1+lenP] = cP[i];
for (i=0; i<lenP; ++i)
cP[i+2] = '0';
cP[1] = '.';
cP[0] = '0';
}
strcpy(product, cP);
}
int main()
{
char a[MAX];
int n;
int k;
int d=1;
int j,begin,end;
int i,t;
while(scanf("%s%d",a,&n)!=EOF)
{
k=0;
char ans[MAX*2]="1.0";
for(i=1;i<=n;i++)
{
FloatMultiplication(ans,a,ans);
}
for(i=0;ans[i]!='\0';i++)
{
if(ans[i]!='0'){begin=i;break;}
}
for(i=0;ans[i]!='\0';i++)end=i;
for(i=end;i>=0;i--)
{
if(ans[i]=='.')
{
end=i-1;
break;
}
if(ans[i]!='0')
{
end=i;
break;
}
}
for(i=begin;i<=end;i++)cout<<ans[i];
cout<<endl;
}
return 0;
}
/*power by gdut_chc*/
[b]高精度2[/b]
整数>>字符串(高精度常用):
string int_to_str(int a) //整数转为字符串
{
string result;
while(a)
{
result.insert(0,1,a%10+'0');
a /= 10;
}
return result;
}
字符串>>整数:
int str_to_int(string a)
{
int result = 0;
int s = 0;
while(s<a.size())
result = result*10 + a[s++]-'0';
return result;
}
加法:
string high_add(string a,string b)
{
a.insert(0,1,'0');
b.insert(0,1,'0');
if(a.size()>b.size())
b.insert(0,(int)a.size()-(int)b.size(),'0');
else if(a.size()<b.size())
a.insert(0,(int)b.size()-(int)a.size(),'0');
int ci = 0;
int N = a.size();
int x = 0;
for(int i = N-1;i>=0;i--)
{ x = a[i]-'0'+b[i]-'0'+ci;
if(x>=10)
{ ci = x/10; //顺序不能颠倒
x = x%10;
}
else
ci = 0;
a[i] = x + '0';
}
while(1)//输出控制
{ if(a[0]=='0'&&a.size()!=1)
{
a.erase (a.begin (),a.begin ()+1);
}
else break;
}
return a;
}
减法:(直接利用加法)
乘法:
string high_mut(string a,string b) //还利用了vector<int>,算法有待改进
{
vector<int> X; //转换到vector<int> 中
vector<int> Y;
int N = a.size();
X.assign(N,0);
int M = b.size();
Y.assign(M,0);
for(int i = 0;i<N;i++)
X[i] = a[i]-'0';
for(int i = 0;i<M;i++)
Y[i] = b[i]-'0';
vector<int> R;
R.assign(N+M,0);
//计算
string result;
int c = 0;
for(int i = M-1;i>=0;i--)
{
int site = N+M-1;
for(int j = N-1;j>=0;j--)
{
R[site-c] += X[j]*Y[i];
site--;
}
c++;
}
for(int i = N+M-1;i>=0;i--)
{
if(R[i]>=10)
{
R[i-1] += R[i]/10;
R[i] = R[i]%10;
}
result.insert(0,1,R[i]+'0');
}
//输出控制
while(1)
{
if(result[0]=='0'&&result.size()!=1)
result.erase (result.begin (),result.begin ()+1);
else break;
}
return result;
}
除法:
string high_div(string a,int b) //获得a/b的结果,类似整除,但是是用字符实现
{
string result;
int temp = a[0] - '0';
int s = 1;
go_on:
while(temp<b&&s<a.size())
{
temp = temp*10 + a[s++] - '0';
if(temp<b&&s<a.size())
result += '0';
}
result += temp/b + '0';
temp = temp%b;
if(s <a.size())
goto go_on;
else
{
while(result[0] == '0'&&result.size()>1)
result.erase(result.begin());
}
return result;
}
模除 :
int high_mod(string a,int b)
{
string result;
int temp = a[0] - '0';
int s = 1;
go_on:
while(temp<b&&s<a.size())
{
temp = temp*10 + a[s++] - '0';
if(temp<b&&s<a.size())
result += '0';
}
result += temp/b + '0';
temp = temp%b;
if(s <a.size())
goto go_on;
else
return temp;
}
阶乘:
string high_n(int m)
{
int n = m;
string sum = "1";
for(int i = n;i>=2;i--)
{
string temp = int_to_str(i); //调用转换函数
sum = high_mut(sum,temp);//高精度乘法
}
return sum;
}
进制转换:
string base_m(int n,int m)//把n转换为m(m<=36)进制数,最大数用Z表示
{
string temp; //保存n的进制数
string temp2;
int x = 0;
while(n >= m)
{
x = n%m;
n = n/m;
temp2 += '0'+x;
if(temp2[0] > '9')
temp2[0] = 'A'+x-10;
temp.insert(0,temp2);
temp2.clear();
}
temp2 += '0'+n;
if(temp2[0] > '9')
temp2[0] = 'A'+n-10;
temp.insert(0,temp2);
return temp;
}
[b]高精度3[/b]
JustForPlay 2004.3.8
// 无符号整数的高精度运算
#include < stdio.h >
#include < string.h >
const int DEV = 10000 ; //定义每个整数存放大整数中四位(104)
const int MAXN = 1000 ; //最大整数长度 MAXN×4
struct BIG_INT {
int bit [ MAXN ], cnt;
bool positive ;
BIG_INT (){ cnt=0; positive = true ; memset ( bit , 0 , sizeof ( bit ) ); }
//-------------------------------------------------输入字符串,转化为大整数的表示
void Input () {
char ts[1000] ;
scanf ( "%s", ts);
int i = 0 , j = 0 , pt = 0 ;
int len = strlen (ts);
for ( i = len-1 ; i >=0 ; i -= 4 )
{
int temp = 0 ;
for ( j = ( i-3 >= 0)?i-3:0 ; j <= i; j++ )
temp = temp * 10 + ts[j] - '0';
bit[ pt++ ] = temp ;
}
cnt = pt ;
while ( cnt > 0 && bit [ cnt - 1 ] == 0 ) cnt -- ;
}
//------------------------------------------------输出大整数
void print () {
int i ;
if( cnt == 0 ) printf ( "%d\n", 0 );
else{
printf ( "%d", bit[cnt-1] );
for ( i=cnt-2 ; i >=0; i -- )
printf ( "%04d", bit );
printf ("\n") ;
}
}
//------------------------------------------------------加法
void add ( BIG_INT& fir, BIG_INT& sec ) {
cnt = 0 ;
int left = 0 ;
int pt = 0 ;
int temp = 0 ;
while ( pt < fir.cnt || pt < sec.cnt || left >0 ) {
temp = left ;
if ( pt < fir.cnt ) temp += fir.bit[pt] ;
if ( pt < sec.cnt ) temp += sec.bit[pt] ;
bit[ pt++ ] = temp%DEV ;
left = temp/DEV ;
}
cnt = pt;
}
// ----------------------------------减法,必须fir>sec !
void sub ( BIG_INT& fir, BIG_INT& sec ) {
cnt = 0 ;
int left = 0 ;
int pt = 0 ;
int temp = 0 ;
while ( pt < fir.cnt ){
temp = fir.bit[pt] - left ;
if ( pt < sec.cnt ) temp -= sec.bit[pt] ;
if( temp < 0){ temp += DEV; left = 1;}
else left = 0;
bit [pt++] = temp ;
}
cnt = pt ;
while ( bit[cnt-1] == 0 ) cnt-- ;
}
//---------------------------------乘法
void multiple ( BIG_INT& fir, BIG_INT& sec ) {
int i , j ;
cnt = 0 ;
memset ( bit , 0 ,sizeof ( bit ) );
int left = 0 ;
int temp = 0 ;
for ( i = 0 ; i < fir.cnt ; i++ )
{
left = 0 ;
for ( j = 0 ; j < sec.cnt ; j++ )
{
temp = left + fir.bit [ i ] * sec.bit [ j ] + bit [ i+j ];
bit [ i+j ] = temp % DEV ;
left = temp / DEV ;
}
while ( left > 0 )
{
temp = left + bit [ i+j ] ;
bit [ i+j ] = temp % DEV;
left = temp / DEV;
j++ ;
}
}
for ( i = MAXN - 1 ; i >= 0 ; i-- )
if ( bit [ i ] > 0) break;
cnt = i + 1 ;
}
// --------------------------------------比较fir与sec 大小
//--------------------------------------If ( fir < sec ) return true
bool smaller ( BIG_INT& fir, BIG_INT& sec){
while ( fir.cnt > 0 && fir.bit [ fir.cnt - 1 ] == 0 ) fir.cnt -- ;
while ( sec.cnt > 0 && sec.bit [ sec.cnt - 1 ] == 0 ) sec.cnt -- ;
if ( fir.cnt < sec.cnt ) return true;
else if ( fir.cnt > sec.cnt ) return false;
int i ;
for ( i = fir.cnt - 1 ; i >= 0 ; i -- ){
if ( fir.bit [ i ] < sec.bit [ i ] ) return true;
else if ( fir.bit [ i ] > sec.bit [ i ] ) return false;
}
return false;
}
// ----------------------------------------------返回 fir/sec 且fir = fir%sec
// -------------------------------------------- 供 devide 调用
int get_div ( BIG_INT& fir, BIG_INT& sec ){
int pt = 0 ;
BIG_INT tadd ;
while ( 1 ) {
if ( smaller ( fir, sec ) ) return pt ;
tadd.sub ( fir , sec );
fir = tadd ;
pt ++ ;
}
}
//-----------------------------------------------除法
void Devide ( BIG_INT& fir, BIG_INT& sec, BIG_INT& remd ) {
if( sec.cnt == 0 || ( sec.cnt == 1 && sec.bit [0] == 0 ) ) {
printf ( "Devide Error ! \n" ) ;
return ;
}
cnt = 0 ;
remd.cnt = 0;
memset ( bit , 0 , sizeof ( bit ) ) ;
memset ( remd.bit , 0 , sizeof ( remd.bit ) ) ;
int i , j ;
for ( i = fir.cnt - 1 ; i >= 0 ; i-- )
{
for ( j = remd.cnt - 1 ; j >= 0 ; j -- ) remd.bit [ j+1 ] = remd.bit [ j ] ;
remd.bit [ 0 ] = fir.bit [ i ] ;
remd.cnt ++ ;
int ret_n = get_div ( remd , sec ) ;
bit [ i ] = ret_n ;
}
for ( i = fir.cnt - 1 ; i >= 0 ; i-- )
if ( bit [ i ] > 0 ) break ;
cnt = i + 1 ;
}
};
//----------------------------------------------------调用参考
int main()
{
BIG_INT fir,sec,res;
while(1)
{
fir.Input();
sec.Input();
BIG_INT rem;
res.Devide ( fir , sec , rem );
res.print();
rem.print();
}
return 0;
}
[b]高精度4[/b]
基本方法
1、加数、减数、运算结果的输入和存储
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。在Pascal中,能表示多个数的数据类型有两种:数组和字符串。
(1)数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;
用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便
用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯;
(2)字符串:字符串的最大长度是255,可以表示255位。
用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;
用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便;
(3)因此,综合以上所述,对上面两种数据结构取长补短:用字符串读入数据,用数组存储数据:
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)}
对s2的转化过程和上面一模一样。
2、运算过程
在往下看之前,大家先列竖式计算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;
3、结果的输出(这也是优化的一个重点)
按运算结果的实际位数输出
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: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.
高精度减法
1、和高精度加法相比,减法在差为负数时处理的细节更多一点:当被减数小于减数时,差为负数,差的绝对值是减数减去被减数;在程序实现上用一个变量来存储符号位,用另一个数组存差的绝对值。
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存被减数,符号存符号}
▲将字符串处理成数值:
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;
▲打印结果:
例:差:第一位是12,第二位是234,第三位是1234,最后一位:3。它的实际数值是12023412340003。
从上例可以看出:打印时,从第二位开始,因为每一段都代表4位,不足4位的要补足0。
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;
高精度乘法和阶乘
一、高精度乘法基本思想和加法一样。其基本流程如下:
①读入被乘数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);
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 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.
二、求N!当N稍微大一点时结果就很大了(如n=10时N!=3628800;n=20时N!=2432902008176640000);因此,求阶乘通常都要用到高精度算法。基本过程和乘法一样,只是多了控制乘法次数的一层循环。
var
a,b,c:array[1..1000] of integer;
i,j,l,m,k1,k2,x,y,z,w,n,t:integer;
procedure chengfa; {高精度乘法}
begin
for l:=1 to k1 do
for m:=1 to k2 do
begin
x:=a[l]*b[m];
y:=x div 10;
z:=x mod 10;
w:=l+m-1;
c[w]:=c[w]+z;
c[w+1]:=c[w+1]+y+c[w] div 10;
c[w]:=c[w]mod 10
end;
k1:=k1+k2; {位数为K1、K2的两数相乘最大只有K1+K2位}
if c[k1]=0 then k1:=k1-1; {如果最高位为0则位数减少一位}
for t:=1 to k1 do a[t]:=c[t]; {把一次高精度相乘的结果放到A数组中,以便下次相乘}
for t:=1 to k1 do c[t]:=0; {同时把数组C清空,以便下次相乘,因为每调用此过程一次都是一次 全新的高精度乘法,所以数组C必须清空}
end;
procedure zhuanhuan; {把I每一位分解开分别赋值给数组B的每一个元素}
begin
if i>=100 then
begin
k2:=3;
b[3]:=i div 100;
b[2]:=(i-b[3]*100) div 10;
b[1]:=i-b[3]*100-b[2]*10
end
else
if i>=10
then
begin
k2:=2;
b[2]:=i div 10;
b[1]:=i-b[2]*10
end
else
begin
k2:=1;
b[1]:=i
end;
end;
begin
writeln('input:');
readln(n);
a[1]:=1; {最后结果放在数组A中}
k1:=1;
for i:=2 to n do
begin
zhuanhuan;
chengfa;
end;
writeln(n:2,'!= ');
for i:=k1 downto 1 do write(a)
end.
高精度除法(1)
{
author : tenshi
date : 2002-03-10
problem : high precision --- divide
algorithm : simple
input : two lines,
1st line : a positive highprecision number
2nd line : a positive low precicion number
output : times & rest of the two numbers
}
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)
{
author : tenshi
date : 2002-03-10
problem : high precision --- divide
algorithm : simple
input : two lines, each line a positive highprecision number
output : quotient & modulus of the two highprecision numbers
}
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.