实现一种算法,将实数转换为#F中的连续分数

时间:2018-03-17 12:09:36

标签: recursion f# continued-fractions

我正在尝试实现一个递归函数,它接受一个浮点数并返回一个表示浮点数的连续分数表示的整数列表(https://en.wikipedia.org/wiki/Continued_fraction)。一般来说,我想我理解该算法应该如何工作。它相当简单。到目前为止我所拥有的是:

let rec float2cfrac (x : float) : int list = 
    let q = int x
    let r = x - (float q)
    if r = 0.0 then
         []
    else 
         q :: (float2cfrac (1.0 / r ))

问题在于基本情况显然。似乎值r永远不会减少到0.0而是算法继续返回值等于0.0 ..... [数字]。我只是不确定如何进行比较。我究竟该怎么做呢。该函数所基于的算法表示基本情况为0,因此我自然将其解释为0.0。我没有看到任何其他方式。另外,请注意这是一个赋值,我明确要求我递归地实现算法。有没有人对我有一些指导?非常感谢

1 个答案:

答案 0 :(得分:2)

  

似乎值r永远不会减少到0.0而是算法继续返回值,如0.0 [数字]。

这是浮点比较的经典问题。您需要使用一些 epsilon 容差值进行比较,因为r永远不会完全 0.0

let epsilon = 0.0000000001
let rec float2cfrac (x : float) : int list =
  let q = int x
  let r = x - (float q)
  if r < epsilon then
    []
  else 
    q :: (float2cfrac (1.0 / r))

> float2cfrac 4.23
val it : int list = [4; 4; 2; 1]

有关详情,请参阅this MSDN documentation

您可以为此定义辅助函数:

let withinTolerance (x: float) (y: float) e =
  System.Math.Abs(x - y) < e

另请注意,您的原始解决方案不是尾递归的,因此它会在递归时消耗堆栈并且可能会溢出堆栈。您可以重构它,以便float可以unfold而无需递归:

let float2cfrac (x: float) =
  let q = int x
  let r = x - (float q)
  if withinTolerance r 0.0 epsilon then None
  else Some (q, (1.0 / r))

4.23 |> Seq.unfold float2cfrac // seq [4; 4; 2; 1]