求助: 关于递归时的记忆化缓存
大佬们,中午好😄 🙏
咱就开门见山了,集算器能否实现类似 Python 的缓存装饰器 @lru_cache,用于记录递归时一些重复分支的结果。
比如,有这么一个题:
给定一个整数 N,5 ≤ N ≤ 100,将 N 分解为若干个互不相同的正整数的和,使得这些数的乘积最大,求这个最大的乘积。
当然这个题有其他解法,举例是为了说明记忆化缓存对递归效率的影响,这里只用递归实现。
我们先观察无记忆化的递归,写法很简洁,注意以下代码不要先运行 fx(100,2) 会很慢:
func fx(n,p) n表示某个正整数,p表示起始值是2每次加1的数
if p>n || p<2 return 1
>skip=fx(n,p+1) 不选当前p
>pick=p*fx(n-p,p+1) 选择当前p, p要相乘
>mx=max(skip,pick) 取两种状态中较大的
return mx
=fx(10,2)
以上写法在 N 较小的时候没有问题,但随着 N 的增大,效率急剧下降,当 N=100 时,需耗时 60 秒。因为 N 越大分支越多,重复计算也就越多,所以手工模拟记忆化缓存,得到如下写法:
=cache=create(#K,V) =hits=[0] 缓存命中次数
func fx(n,p) n表示某个正整数,p表示起始值是2每次加1的数
if p>n || p<2 return 1
>K=n/$[,]/p
if (M=cache.find(K).V)!=null >hits(1)+=1
return M
>skip=fx(n,p+1) 不选当前p
>pick=p*fx(n-p,p+1) 选择当前p, p要相乘
>mx=max(skip,pick) 取两种状态中较大的
>cache.modify(0,K,mx) 更新缓存K,mx
return mx
=fx(100,2)
>output("缓存命中次数: " / hits)
cache 是一张表,用递归函数的参数作为键,对应的计算结果作为值。每次递归的时候检查有没有命中缓存,命中就直接返回结果,没有命中时继续递归。这个方法在 N=100 时有明显助益,可以看到,原来耗时 60 秒,现在只需 600 毫秒。代码中的 hits 记录了缓存命中次数,可以查看当 N=100 时,缓存命中了 1551 次。
但以上手工模拟记忆化缓存的写法,似乎并不是万能良药,因为缓存是个表,随着记录的分支越来越多,这个表也会占用越来越多的内存。所以,想恳请大佬们看看,有没有可能在应用程序层面实现并管理记忆化缓存,像 Python 的装饰器 @lru_cache,它用的是 "最近最少使用" 策略来管理缓存。那我想着咱集算器能不能在写递归的时候也能够实现这样的装饰器。还是挺羡慕这个功能的,有些场景用记忆化递归确实能助力代码运行效率,比如,常见的斐波那契递归写法,还有什么货币兑换等等,都可以用此方法实现较高效率的运行。
上述代码片段可直接复制到集算器运行,恳请大佬们得闲时看看,能否在应用程序层面实现记忆化缓存,谢谢!
软引用还不是很好弄,动静太大了,先记着了。
这事和递归倒没什么关系。
好嘞,谢谢大神关注🙏 😄