[英]How to optimize runtime on recursive Racket function to determine maximum of element in list?
這是我出色且有效的 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]))
這將輸入長度的 big-O 復雜度從指數變為線性。
還有其他潛在的優化,例如利用尾調用,但這些不如保存中間結果以避免重新計算遞歸那么重要。
如何設計程序 2e 圖 100:使用本地定義提高性能中也描述了這種使用local
定義提高性能的方法。
您可以使用time-apply
來測量運行時間。 這是一個過程,它將調用具有大列表的給定函數並返回time-apply
所做的結果:
(define (time-on-list f size #:initial-element (initial-element 0)
#:trials (trials 10)
#:verbose (verbose #f)
#:gc-times (gc-times '()))
(define pre-gc (if (memv 'pre gc-times) #t #f))
(define post-gc (if (memv 'post gc-times) #t #f))
(when verbose
(printf "trials ~A
pre-gc ~A (not counted in runtime)
post-gc ~A (counted-in-runtime)~%"
trials
pre-gc
post-gc))
;; Intentionally construct a nasty list
(define ll (list (for/list ([i (in-range size)]) i)))
(define start (current-milliseconds))
(when (and post-gc (not pre-gc))
(collect-garbage 'major))
(let loop ([trial 0] [cpu 0] [real 0] [gc 0])
(if (= trial trials)
(values (/ cpu trials 1.0) (/ real trials 1.0) (/ gc trials 1.0))
(begin
(when pre-gc
(collect-garbage 'major))
(when verbose
(printf " trial ~A at ~Ams~%" (+ trial 1) (- (current-milliseconds)
start)))
(let-values ([(result c r g)
(time-apply (if post-gc
(λ (l)
(begin0
(f l)
(collect-garbage 'major)))
f)
ll)])
(loop (+ trial 1) (+ cpu c) (+ real r) (+ gc g)))))))
您可以將其與不同的size
值一起使用,以了解性能。 默認情況下,它平均超過 10 次試驗,但可以調整。 您還可以在過程中的各個點要求 GC,但您可能不應該這樣做。 這是基於我用來測試事物性能的過程:它不是特別完成的代碼。
您幾乎肯定不想在函數的大大小值上運行它:請參閱其他答案。 特別是,以下是您的函數長度最長為 25 的列表的時間:
(0 0 0 0 0 0 0 0 0 0.1 0.1 0.2 0.4 0.9 1.9 3.5
6.7 13.6 29.7 54.3 109.8 219.7 436.6 958.1 2101.4)
這應該讓你相信有些事情是非常錯誤的!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.