在使用Viete'计算Pi近似值时,卡在ocaml的无限循环中;s公式
我在计算给定精度的Pi近似值时遇到了问题。我已经得出结论,无限循环问题是由我的循环退出条件引起的,但是我不知道确切的问题是什么。在我看来,退出条件应该是在使用Viete'计算Pi近似值时,卡在ocaml的无限循环中;s公式,ocaml,pi,approximation,Ocaml,Pi,Approximation,我在计算给定精度的Pi近似值时遇到了问题。我已经得出结论,无限循环问题是由我的循环退出条件引起的,但是我不知道确切的问题是什么。在我看来,退出条件应该是 abs(current_aproximation - previous_approximation) < precision abs(当前近似值-先前近似值)
abs(current_aproximation - previous_approximation) < precision
abs(当前近似值-先前近似值)
代码如下:
let pi(prec) =
let rec loop(curr, prev) =
if(abs_float( (2. /. curr) -. (2. /. prev) ) < prec) then // problematic line
(2. /. curr)
else
loop(curr *. sqrt(2. +. curr) /. 2., curr)
in loop(sqrt(0.5), 1.);;
让pi(prec)=
让rec循环(当前,上一个)=
如果(abs_float((2./.curr)-(2./.prev))
感谢您提供有关解决此问题的任何提示。如果您修改代码以打印出
curr
的值,您会发现您很快就到达了一个固定点:
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
(...)
根据你的初始值应该是(sqrt 2.)/。2.
而不是sqrt(0.5)
。但即使进行了这种修改,也不可能要求精度小于0.61
,而不达到前面描述的固定点
我的猜测是,
float
s不够精确,无法以这种方式表示此算法。您正在计算一个不同的乘积:在一次迭代之后,您应该计算sqrt(2)/2*sqrt(2+sqrt(2))/2,但您计算sqrt(2)/2*sqrt(2+sqrt(2)/2)/2
这个算法怎么样
let pi prec =
let rec p2 xn root =
let nroot = sqrt(2. +. root) in
let xm = xn *. (nroot /. 2.) in
if (abs_float (( 2. /. xm ) -. (2. /. xn))) < prec
then xm
else p2 xm nroot
in 2. /. (p2 1.0 0.0)
sqrt(2)/2
和sqrt(0.5)
是相等的,浮动精度不会导致此类错误。算法实现错误。非常感谢。我一定错过了计算的那一部分。我还有一个附带的问题-当传入递归函数的参数较少时,代码的性能是否会提高?还是这只是个人偏好?您的意思是p2作为闭包绑定prec与将prec作为附加参数添加到内部函数之间的区别?我想这取决于编译器,但我可以想象闭包会被翻译成第二个版本。在这种特殊情况下,p2无论如何只创建一次。
# pi 0.1 ;;
- : float = 3.12144515225805197
# pi 0.01 ;;
- : float = 3.14033115695475251
# pi 0.001 ;;
- : float = 3.14127725093277288
# pi 0.0001 ;;
- : float = 3.14157294036709134