recursion - 如何优化递归球拍函数的运行时间以确定列表中元素的最大值?
问题描述
这是我的精彩且有效的 LISP 球拍“中间与 lambda”样式递归函数,用于确定列表中符号值最高的符号。
(define maximum
(lambda [x]
(cond
[(empty? x) 0]
[(cons? x)
(cond
[(>= (first x) (maximum (rest x))) (first x)]
[else (maximum (rest x))]
)
]
)
)
)
(check-expect (maximum '(1 2 3)) 3)
(check-expect (maximum '(1)) 1)
(check-expect (maximum '(0)) 0)
如何检查和优化运行时?
运行时的递归与迭代有什么不同吗?
谢谢您的回答!
亲切的问候,
解决方案
有一件主要的事情会大大提高性能,将其从指数时间变为线性时间。
不要重新计算递归,将其保存为中间结果。
在内部cond
表达式中,(maximum (rest x))
计算两次。一次是第一个分支的问题,一次是第二个分支的答案。
(cond
[(>= (first x) (maximum (rest x))) (first x)]
[else (maximum (rest x))])
在第一个问题为假的常见情况下,(maximum (rest x))
将重新计算,使其必须做的工作加倍。更糟糕的是,在最坏的情况下,当最大值位于末尾时,这种加倍可能发生在每个递归级别。这就是使它呈指数增长的原因。
要解决此问题,您可以使用local
定义和命名中间结果。
(local [(define maxrst (maximum (rest x)))]
(cond
[(>= (first x) maxrst) (first x)]
[else maxrst]))
这使得输入长度的大 O 复杂度从指数变为线性。
还有其他潜在的优化,例如利用尾调用,但这些不如保存中间结果以避免重新计算递归那么重要。
这种使用local
定义提高性能的方法也在如何设计程序 2e 图 100:使用本地来提高性能中进行了描述。
推荐阅读
- reactjs - 在 Redux 中更新递归对象状态
- javascript - 为什么我的包装器会破坏它所包装内容的 CSS?
- pandas - 如何更改散点矩阵中的图例标签
- android - 如何更改 ML 模型绑定的浮点精度?
- dart - Dart 中未打印的未来价值
- reactjs - Firestore get() 和 onSnapshot 函数在 useEffect 中不起作用
- php - 服务器说:当我从移动设备访问该站点时,在握手过程中未定义索引 Sec-WebSocket-Key。从同一台电脑(本地主机)可以
- mysql - 通过nodejs从多个mysql表中获取数据到hbs文件中
- fancytree - 花式树文件夹图标不显示
- json - 错误:文档类型声明包含或指向的标记声明必须格式正确