主题:[NOIP热身竞赛]高精度开平方
maxumi
[专家分:2200] 发布于 2007-08-28 13:20:00
求sqrt(n)的值,精确到小数点后500位。(2<=n<=32767)
输入(sqrtx.in):
仅1行, 为n.
输出(sqrtx.out):
为sqrt(n).
样例输入:
2
样例输出:
1.41421356237309........(太长,不贴了)
时间限制: 30秒(够宽松了吧......)
板凳
angwuy [专家分:2280] 发布于 2007-08-31 19:33:00
1. 一般方法
很类似除法,以求200的开平方为例。
1 4. 1 4 2…… {以小数点为界,每隔2位写一位得数, 注意加小数点}
√2`00. {以小数点为界,每隔2位做一个标记(其实做不做没所谓)}
1 1 {算出不大于最右一组数的开平方的最大整数,写在标记左上方,
即 Int( sqrt(最右一组数) ), 并把这个整数的平方写下1}
100 {计算它们的差, 在右边添两个零}
24 96 {将刚才求得的一位数乘以20(即1*20)然后,算出不大于差的x(20+x),
的x的最大整数 4 }
400 {计算它们的差, 在右边添两个零}
281 281 {将求得的数乘以20(即14*20)然后,算出不大于差的x(280+x),
的x的最大整数 1 }
11900 {计算它们的差, 在右边添两个零}
2824 11296 {同上, 算出不大于差的x(141*20+x),的x的最大整数 4}
60400
28282 56564
3826
……
2. 无穷级数 (x是被开方数)
Sqrt(x)=a/b*1/Sqrt(1-(xb2-a2)/(xb2))
而1/sqrt(1-y) = 1+(1/2)y+(1*3)/(2*4)y2+(1*3*5)/(2*4*6)y3+…
a/b是Sqrt(x)的近似值, 例如Sqrt(2)≈239/169 ,a=239,b=169 ,得Sqrt(2) = (239/169)*1/Sqrt(1-1/57122)
下面是一个极差的程序,但反映了上述算法的基本思想。
Program SquareRoot_n;
Type PosInt=0..MaxLongInt;
const R=10000; { 基数 }
LR=4; { 基数的常用对数 }
NRDigits=1000; {计算的位数}
size=1+Trunc(NRDigits/LR);
Var i:Integer;
Procedure SetToInteger (n:LongInt;Var x:Array of LongInt; Inte:LongInt);
Var i:PosInt;
Begin
fillchar(x,sizeof(x),0); {for i:=1 To n DO x:=0;}
x[0]:= Inte;
End;
{ X 等于0 ? }
Function IsZero (n:LongInt; x:Array of LongInt):Boolean;
var i:Word;
Begin
for i:=0 To n-1 Do if x=R Then Begin carry:= Trunc(xi/R); Dec(xi,carry*R); End
else carry:= 0;
x:= xi;
End;
End;
{ X ← X / d }
Procedure Division (n:LongInt;Var x:array of LongInt; d:LongInt);
Var carry, xi, q, i :PosInt;
Begin
carry:=0; q:=0; xi:=0;
for i:=0 To n-1 Do Begin
xi := x+carry*R;
q:=xi Div d; {q:= Trunc(xi/d);}
carry := xi-q*d;{carry:=xi Mod d;}
x:= q;
End;
End;
{ Print X }
Procedure Print (n:LongInt; d:PosInt; x:array of LongInt);
Var i:PosInt ; f:text;
Begin
Assign(f,'d:\sqr.txt'); rewrite(f);
Writeln(f,'Sqrt(',d,')=',Trunc(Sqrt(d)),'.');
for i:=1 To n-1 Do Begin
If x0 do begin
Mul(X,X,Tmp);
Mul(Tmp,A,Tmp); {每次只取比X多一倍位数的A}
Tmp ← 1-Tmp; {for i=1 to size do tmp0 Then Begin IsZero:=true; Exit; End;
IsZero:=false;
End;
{ 计算 X += Y }
Procedure Add (n:LongInt;Var x:Array of LongInt; y:Array of LongInt);
Var carry,i :PosInt;
Begin
carry:=0;
for i:=n-1 DownTo 0 Do Begin
Inc(x,y+carry);
if x=R Then Begin carry:= Trunc(xi/R); Dec(xi,carry*R); End
else carry:= 0;
x:= xi;
End;
End;
{ X ← X / d }
Procedure Division (n:LongInt;Var x:array of LongInt; d:LongInt);
Var carry, xi, q, i :PosInt;
Begin
carry:=0; q:=0; xi:=0;
for i:=0 To n-1 Do Begin
xi := x+carry*R;
q:=xi Div d; {q:= Trunc(xi/d);}
carry := xi-q*d;{carry:=xi Mod d;}
x:= q;
End;
End;
{ Print X }
Procedure Print (n:LongInt; d:PosInt; x:array of LongInt);
Var i:PosInt ; f:text;
Begin
Assign(f,'d:\sqr.txt'); rewrite(f);
Writeln(f,'Sqrt(',d,')=',Trunc(Sqrt(d)),'.');
for i:=1 To n-1 Do Begin
If x<1000 Then Write(f,'0');
If x<100 Then Write(f,'0');
If x<10 Then Write(f,'0');
Write (f,x);
if i Mod 25=0 Then Writeln (f,' ', i*LR);
End;
Close (f);
End;
{找 a,b}
Procedure fraction(x:integer; Var a:Posint; Var b:PosInt; Var c:PosInt);
Var i,max,ga,gb :PosInt;
m,best,mid,y :Real;
Begin
max:=Trunc(400/x);
best:=1;
y:=Sqrt(x);
For i:=10 to max do Begin
m:=Abs(Trunc(i*y+0.5)/i-y); {m:=Abs(Round(i*y)/i-y);}
If m best:=m;
ga:=Trunc(i*y+0.5); gb:=i;
mid:=gb*gb*x-ga*ga;
If Abs(mid-1)<1e-9 Then Begin a:=ga; b:=gb; c:=b*b*x; End;
End;
End;
End;
Procedure main;
Var r2 :Array[0..size+1] of LongInt;
uk :Array[0..size+1] of LongInt;
a,b,c,k,x :PosInt;
Begin
k:=1;
Readln(x);
fraction(x, a, b, c);
Writeln('x=',x,' a=',a,' b=',b,' c=',c);
SetToInteger (size, r2, 1);
SetToInteger (size, uk, 1);
while IsZero(size, uk) Do Begin
Division (size, uk, c);
Division (size, uk, 2*k);
Mul (size, uk, 2*k-1);
Add (size, r2, uk);
k:=k+1;
End;
Mul (size, r2, a);
Division (size, r2, b);
Print (size, x, r2);
End;
Begin
Main;
End.[/quote]
参考资料:http://www.mydrs.org/dv7/dispbbs.asp?BoardID=13&ID=30783&replyID=441968&skin=1