1

我正在尝试打破下面的这种循环依赖,但不确定最好的方法。

let cashOpeningBalance t =
if t = 1 then
    0.0
else
    cashClosingBalance (t - 1)

let cashInterest t = 
    cashOpeningBalance t * 0.03 

let accumulatedCash t =
    cashOpeningBalance t + cashInterest t

// let moreComplicatedLogic t = ...

let cashClosingBalance t =
    accumulatedCash t

使用这个答案中的一些逻辑,我想出了以下解决方案,但性能真的很差。

let cashOpeningBalance t cashClosingBalance =
if t = 1 then
    10.0
else
    cashClosingBalance (t - 1)

let cashInterest t cashClosingBalance = 
    (cashOpeningBalance t cashClosingBalance) * 0.03 

let accumulatedCash t cashClosingBalance =
    (cashOpeningBalance t cashClosingBalance) + (cashInterest t cashClosingBalance)

// let moreComplicatedLogic t cashClosingBalance = ...

let rec cashClosingBalance t =
    //accumulatedCash t cashClosingBalance
    let temp = accumulatedCash t cashClosingBalance
    printfn "Cash Closing Balance = %f Where t = %i" temp t
    temp

cashClosingBalance 3
(*
> 
Cash Closing Balance = 10.300000 Where t = 1
Cash Closing Balance = 10.300000 Where t = 1
Cash Closing Balance = 10.609000 Where t = 2
Cash Closing Balance = 10.300000 Where t = 1
Cash Closing Balance = 10.300000 Where t = 1
Cash Closing Balance = 10.609000 Where t = 2
Cash Closing Balance = 10.927270 Where t = 3
val it : float = 10.92727
*)

cashClosingBalance 50
(*
Takes a really long time 
*)

无论如何要重写 cashClosingBalance 函数来停止过多的递归调用,如下面的输出所示?我真的需要能够输入高达 400 的 t 值,并且它仍然可以在几秒钟内运行。

4

1 回答 1

4

问题实际上并不在于您moreComplicatedLogic在中间(因此写 big 不方便let rec)。问题是您的代码以低效的方式实现动态编程算法

递归调用最终cashClosingBalance会使用相同的参数多次调用(而不是只调用一次并将结果存储在某个本地缓存中)。在函数式编程中,您可以使用一个相当普遍的记忆化概念来解决这个问题,您可能能够以不同的方式重写您的算法以使其更有效。

如果你想使用记忆,那么你需要这样的东西——下面的助手接受一个函数并创建一个相同类型的函数来缓存以前调用的结果:

let memoize f = 
  let dict = System.Collections.Generic.Dictionary<_, _>() 
  fun v ->
    match dict.TryGetValue(v) with
    | true, res -> res
    | _ -> 
        let res = f v 
        dict.Add(v, res)
        res

memoize然后您可以使用这样的方式重写您的代码(我只是将所有函数定义包装在memoize其中并更改了参数的顺序,因此这t是最后一个):

let cashOpeningBalance cashClosingBalance = memoize (fun t ->
  if t = 1 then
    10.0
  else
    cashClosingBalance (t - 1))

let cashInterest cashClosingBalance = memoize (fun t -> 
    (cashOpeningBalance cashClosingBalance t) * 0.03 )

let accumulatedCash cashClosingBalance = memoize (fun t ->
    (cashOpeningBalance cashClosingBalance t) + (cashInterest cashClosingBalance t))

// let moreComplicatedLogic t cashClosingBalance = ...

let rec cashClosingBalance = memoize (fun t -> 
    //accumulatedCash t cashClosingBalance
    let temp = accumulatedCash cashClosingBalance t
    printfn "Cash Closing Balance = %f Where t = %i" temp t
    temp)
于 2013-09-25T00:09:37.207 回答