【问题标题】:More Efficient Recursive Tetranacci function in F#F# 中更高效的递归 Tetranacci 函数
【发布时间】:2020-03-05 19:41:21
【问题描述】:

我正在尝试尽可能高效地使用 F# 编写一个 tetranacci 函数,但我想出的第一个解决方案确实效率低下。你能帮我想出一个更好的吗?我如何能够在线性时间内实现这一点?

let rec tetra n =
 match n with
 | 0 -> 0
 | 1 -> 1
 | 2 -> 1
 | 3 -> 2
 | _ -> tetra (n - 1) + tetra (n - 2) + tetra (n - 3) + tetra (n - 4)

【问题讨论】:

标签: f# time-complexity fibonacci c#-to-f# f#-3.0


【解决方案1】:

您可以通过设计一个计算 4 元组下一次迭代的状态的函数来节省成本。然后序列生成器函数Seq.unfold 可用于构建包含每个状态四元组的第一个元素的序列,这是一个“惰性”操作——序列的元素仅在消耗时按需计算。

let tetranacci (a3, a2, a1, a0) = a2, a1, a0, a3 + a2 + a1 + a0
(0, 1, 1, 2) 
|> Seq.unfold (fun (a3, _, _, _ as a30) -> Some(a3, tetranacci a30))
|> Seq.take 10
|> Seq.toList
// val it : int list = [0; 1; 1; 2; 4; 8; 15; 29; 56; 108]

请注意,标准 Tetranacci 序列 (OEIS A000078) 通常会以(0, 0, 0, 1) 的起始状态生成:

// val it : int list = [0; 0; 0; 1; 1; 2; 4; 8; 15; 29]

【讨论】:

    【解决方案2】:

    kaefer's 答案很好,但为什么要停在线性时间?事实证明,您实际上可以实现对数时间,方法是注意到递归可以表示为矩阵乘法:

    [T_n+1]   [0; 1; 0; 0][T_n]
    [T_n+2] = [0; 0; 1; 0][T_n+1]
    [T_n+3]   [0; 0; 0; 1][T_n+2]
    [T_n+4]   [1; 1; 1; 1][T_n+3]
    

    但是然后T_n可以通过应用n次递归来实现,我们可以看到M^n*[T_0; T_1; T_2; T_3]的第一个条目(也就是M^n的右上角),我们可以执行矩阵通过重复平方乘以 O(log n) 时间:

    type Mat =
    | Mat of bigint[][]
        static member (*)(Mat arr1, Mat arr2) =
            Array.init arr1.Length (fun i -> Array.init arr2.[0].Length (fun j -> Array.sum [| for k in 0 .. arr2.Length - 1 -> arr1.[i].[k]*arr2.[k].[j] |]))
            |> Mat
    
        static member Pow(m, n) =
            match n with
            | 0 -> 
                let (Mat arr) = m
                Array.init arr.Length (fun i -> Array.init arr.Length (fun j -> if i = j then 1I else 0I))
                |> Mat
            | 1 -> m
            | _ ->
                let m2 = m ** (n/2)
                if n % 2 = 0 then m2 * m2
                else m2 * m2 * m
    
    let tetr =
        let m = Mat [| [|0I; 1I; 0I; 0I|]
                       [|0I; 0I; 1I; 0I|]
                       [|0I; 0I; 0I; 1I|]
                       [|1I; 1I; 1I; 1I|]|]
        fun n -> 
            let (Mat m') = m ** n
            m'.[0].[3]
    
    for i in 0 .. 50 do
        printfn "%A" (tetr i)
    

    【讨论】:

    • 不错的答案。对角化以获得 O(1) 时间!
    【解决方案3】:

    这是一个tail recursive 版本,主要编译成循环(复杂度应该是O(n)):

    let tetr n =
      let rec t acc4 acc3 acc2 acc1 = function
        | n when n = 0 -> acc4
        | n when n = 1 -> acc3
        | n when n = 2 -> acc2
        | n when n = 3 -> acc1
        | n -> t acc3 acc2 acc1 (acc1 + acc2 + acc3 + acc4) (n - 1)
      t 0 1 1 2 n
    

    acc1对应tetra (n - 1)acc2 对应于tetra (n - 2)acc3 对应于tetra (n - 3)acc4对应tetra (n - 4)

    基于the Fibonacci example

    【讨论】:

      猜你喜欢
      • 2016-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-15
      • 2020-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多