简体   繁体   English

F# 中更高效的递归 Tetranacci 函数

[英]More Efficient Recursive Tetranacci function in F#

I am trying to write a tetranacci function using F# as efficiently as possible the first solution I came up with was really inefficient.我正在尝试尽可能高效地使用 F# 编写一个 tetranacci 函数,但我提出的第一个解决方案确实效率低下。 can you help me come up with a better one?你能帮我想出一个更好的吗? How would i be able to implement this in linear time?我如何能够在线性时间内实现这一点?

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)

You could economise by devising a function that computes the state for the next iteration on a 4-tuple.您可以通过设计一个函数来计算 4 元组的下一次迭代的状态,从而节省成本。 Then the sequence generator function Seq.unfold can be used to build a sequence that contains the first element of each state quadruple, an operation that is 'lazy` -- the elements of the sequence are only computed on demand as they are consumed.然后序列生成器函数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]

Note that the standard Tetranacci sequence ( OEIS A000078 ) would usually be generated with the start state of (0, 0, 0, 1) :请注意,标准 Tetranacci 序列 ( OEIS A000078 ) 通常会以(0, 0, 0, 1)的起始状态生成:

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

kaefer's answer is good, but why stop at linear time? kaefer 的回答很好,但为什么要在线性时间停止? It turns out that you can actually achieve logarithmic time instead, by noting that the recurrence can be expressed as a matrix multiplication:事实证明,您实际上可以实现对数时间,注意递归可以表示为矩阵乘法:

[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]

But then T_n can be achieved by applying the recurrence n times, which we can see as the first entry of M^n*[T_0; T_1; T_2; T_3]但是T_n可以通过应用递归n次来实现,我们可以将其视为M^n*[T_0; T_1; T_2; T_3]的第一项M^n*[T_0; T_1; T_2; T_3] M^n*[T_0; T_1; T_2; T_3] M^n*[T_0; T_1; T_2; T_3] (which is just the upper right entry of M^n ), and we can perform the matrix multiplication in O(log n) time by repeated squaring: 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)

Here is a tail recursive version, which compiles to mostly loops (and its complexity should be O(n)):这是一个尾递归版本,它主要编译为循环(其复杂度应为 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 corresponds to tetra (n - 1) , acc2 corresponds to tetra (n - 2) , acc3 corresponds to tetra (n - 3) , acc4 corresponds to tetra (n - 4) acc1对应tetra (n - 1)acc2对应tetra (n - 2)acc3对应tetra (n - 3)acc4对应tetra (n - 4)

Based on the Fibonacci example基于斐波那契示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM