回 帖 发 新 帖 刷新版面

主题:[NOIP热身竞赛]高精度开平方

求sqrt(n)的值,精确到小数点后500位。(2<=n<=32767)

输入(sqrtx.in):
仅1行, 为n.

输出(sqrtx.out):
为sqrt(n).

样例输入:
2

样例输出:
1.41421356237309........(太长,不贴了)

时间限制: 30秒(够宽松了吧......)

回复列表 (共4个回复)

沙发

求一个数a平方根的公式:
X(n + 1) := a                                                        {n = 0}
X(n + 1) := [X(n) + a / X(n)] / 2                                    {n > 0}
这里要求ABS(X(n + 1) - X(n)] <= 1E-500。

把公式里的运算全部变成高精度运算:
X(n + 1) := a                                                        {n = 0}
X(n + 1) := gjddvd((gjdadd(X(n), gjddvd(a, X(n))), 2)                {n > 0}

gjddvd(a, X(n))中的X(n)可能是小数,只有再借助一个函数:
d := intdiv(x, y)
功能是求x DIV y,其中x是整数,y是整数或小数。
这样只能用高精度减法来实现:
x := gjdsubt(x, y);
i := i + 1;
直到x < y。i就是intdiv函数的返回值。

但是,求gjddvd(x, y)必须先把x MOD y算出来,这样又要借助高精度乘法了。

现在,高精度四则运算都扯出来了,程序你们自己编吧……太麻烦了。

板凳

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

3 楼

我提示一下(举个例子):
-0.1 < 7 - 5 * sqrt(2) < 0.1
两边平方,得
-0.01 < 99 - 70 * sqrt(2) < 0.01
再两边平方,得
-0.0001 < 19601 - 13860 * sqrt(2) < 0.0001
剩下的不用我说了吧......

4 楼


好象有点难啊

我来回复

您尚未登录,请登录后再回复。点此登录或注册