Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 如何使这个简单的递归关系(差分方程)尾部递归?_F#_Tail Recursion - Fatal编程技术网

F# 如何使这个简单的递归关系(差分方程)尾部递归?

F# 如何使这个简单的递归关系(差分方程)尾部递归?,f#,tail-recursion,F#,Tail Recursion,如果没有CPS或备忘录,它怎么可能是递归的 let rec f n = match n with | 0 | 1 | 2 -> 1 | _ -> f (n - 2) + f (n - 3) 甚至更好: let f n = Seq.unfold (fun (x, y, z) -> Some(x, (y, z, x + y))) (1I, 1I, 1I) |> Seq.nth n 要了解这里的情况,请参考代码片段。它定义了斐波那

如果没有CPS或备忘录,它怎么可能是递归的

let rec f n =
    match n with
    | 0 | 1 | 2 -> 1
    | _ -> f (n - 2) + f (n - 3)
甚至更好:

let f n = Seq.unfold (fun (x, y, z) -> Some(x, (y, z, x + y))) (1I, 1I, 1I)
          |> Seq.nth n
要了解这里的情况,请参考代码片段。它定义了斐波那契算法,你们的算法非常相似

UPD此处有三个组件:

  • lambda,它获取
    i
    -th元素
  • i
    上运行递归的组合器;及
  • 包装程序启动整个运行,然后提取值(从三元组中,如@Tomas代码中)

  • 您要求使用尾部递归代码,实际上有两种方法:像@Tomas那样制作您自己的组合器,或者利用现有的组合器,
    Seq.unfold
    ,这当然是尾部递归。我更喜欢第二种方法,因为我可以将整个代码拆分为一组
    let
    语句。

    由@bytebuster提出的解决方案很好,但他没有解释他是如何创建的,因此只有在解决这个特定问题时才有帮助。顺便说一句,您的公式看起来有点像斐波那契(但不是完全一样),它可以(即使没有隐藏在
    Seq.unfold
    中的循环)

    您从以下函数开始:

    let lambda (x, y, z) = x, (y, z, x + y)
    let combinator = Seq.unfold (lambda >> Some) (1I, 1I, 1I)
    let f n = combinator |> Seq.nth n
    
    函数为参数
    n-2
    n-3
    调用
    f0
    ,因此我们需要知道这些值。诀窍是使用(可以使用备忘录),但由于您不想使用备忘录,我们可以手工编写

    我们可以编写
    f1 n
    ,它返回一个包含当前值和两个过去值
    f0
    的三元素元组。这意味着
    f1 n=(f0(n-2),f0(n-1),f0 n)

    此函数不是尾部递归函数,但它只递归调用自身一次,这意味着我们可以使用累加器参数模式:

    let rec f1 n = 
      match n with 
      | 0 -> (0, 0, 1)
      | 1 -> (0, 1, 1)
      | 2 -> (1, 1, 1)
      | _ -> 
        // Here we call `f1 (n - 1)` so we get values
        //   f0 (n - 3), f0 (n - 2), f0 (n - 1)
        let fm3, fm2, fm1 = (f1 (n - 1))
        (fm2, fm1, fm2 + fm3)
    
    我们需要处理参数
    0
    1
    ,特别是在
    fc
    的主体中。对于任何其他输入,我们从初始三个值开始(即
    (f0 0,f0 1,f0 2)=(1,1,1)
    ),然后循环n次执行给定的递归步骤,直到达到2。递归
    循环
    函数是@bytebuster的解决方案使用
    Seq.unfold
    实现的


    因此,函数有一个尾部递归版本,但这只是因为我们可以简单地将过去的三个值保存在一个元组中。通常,如果用于计算所需的先前值的代码执行更复杂的操作,则这可能不可能实现。

    即使比尾部递归方法更好,您也可以利用矩阵乘法来减少使用O(logn)运算的解决方案中的任何重复。我把正确性的证明留给读者作为练习

    let f2 n =
      let rec loop (fm3, fm2, fm1) n = 
        match n with 
        | 2 -> (fm3, fm2, fm1)
        | _ -> loop (fm2, fm1, fm2 + fm3) (n - 1)
      match n with
      | 0 -> (0, 0, 1)
      | 1 -> (0, 1, 1)
      | n -> loop (1, 1, 1) n
    

    考虑到
    lambda(n)
    lambda(i)
    无限序列的第n个元素,该解释实际上是对代码中的
    f2
    的代数简化。
    let f2 n =
      let rec loop (fm3, fm2, fm1) n = 
        match n with 
        | 2 -> (fm3, fm2, fm1)
        | _ -> loop (fm2, fm1, fm2 + fm3) (n - 1)
      match n with
      | 0 -> (0, 0, 1)
      | 1 -> (0, 1, 1)
      | n -> loop (1, 1, 1) n
    
    module NumericLiteralG =
        let inline FromZero() = LanguagePrimitives.GenericZero
        let inline FromOne() = LanguagePrimitives.GenericOne
    
    // these operators keep the inferred types from getting out of hand
    let inline ( + ) (x:^a) (y:^a) : ^a = x + y
    let inline ( * ) (x:^a) (y:^a) : ^a = x * y
    
    let inline dot (a,b,c) (d,e,f) = a*d+b*e+c*f
    let trans ((a,b,c),(d,e,f),(g,h,i)) = (a,d,g),(b,e,h),(c,f,i)
    let map f (x,y,z) = f x, f y, f z
    
    type 'a triple = 'a * 'a * 'a
    // 3x3 matrix type
    type 'a Mat3 = Mat3 of 'a triple triple with
        static member inline ( * )(Mat3 M, Mat3 N) = 
            let N' = trans N
            map (fun x -> map (dot x) N') M
            |> Mat3
        static member inline get_One() = Mat3((1G,0G,0G),(0G,1G,0G),(0G,0G,1G))
        static member (/)(Mat3 M, Mat3 N) = failwith "Needed for pown, but not supported"
    
    let inline f n =
        // use pown to get O(log n) time
        let (Mat3((a,b,c),(_,_,_),(_,_,_))) = pown (Mat3 ((0G,1G,0G),(0G,0G,1G),(1G,1G,0G))) n
        a + b + c
    
    // this will take a while...
    let bigResult : bigint = f 1000000