[英]How can I optimize this code in Haskell
我需要您的有關優化的幫助。 這是問題:
使用數字3的10倍無法獲得的最小(嚴格為正)自然數與普通算術運算符相結合會是什么?
這是我的解決方案:
import Ratio
num x 1 = [x]
num x n = num'' $ concat $ [ num' a b | i <- [1..n`quot`2], a <- num x i, b <- num x (n-i) ]
where num' a b | a==0 = [b]
| b==0 = [a]
| otherwise = [a+b,abs(a-b),a*b,a/b,b/a]
num'' (x:r) = x : num'' (filter (x/=) r)
num'' _ = []
cint x = map numerator . filter ((1==) . denominator) . num x
firstNumber x n = take 1 [ i | i <- [1..], i `notElem` (cint x n) ]
但這是如此低效。 例如,當我調用firstNumber 3 7時 ,大約需要30秒才能看到結果。 但是,盡管等待了很長時間,但我沒有看到firstNumber 3 10的結果。 我該如何優化?
我們可以通過生成部分答案的集合來解決該問題: num k 1
是集合{k}, num 2
是通過將num k 1
與num k 1
組合而生成的所有數字的集合, num k 3
是通過組合num k 1
和num k 2
生成的所有數字,依此類推。 每個步驟都使用先前步驟中計算出的集合,並應用一個運算符。 這是前3個步驟。 請注意,每個數字都是使用兩個先前生成的數字和一個運算符計算的。
num 3 1
= {3} num 3 2
= {3-3 = 0,3/3 = 1,3 = 3,3 + 3 = 6,3 * 3 = 9} num 3 3
= {3-9 = -6,3-6 = -3,1-3 = -2,0 = 0,1/3 = 1/3,3/6 = 1/2,1 = 1, 6/3 = 2、3 = 3、3 + 1 = 4、6 = 6、9 = 9、3 + 9 = 12、3 * 6 = 18、3 * 9 = 27} 基於列表的num
函數正在重新計算前面的步驟,原因有兩個。
n
從頭開始重新計算所有先前的步驟。 例如, num x 4
將計算num x 1
, num x 2
和num x 3
。 然后, num x 3
將再次計算num x 1
和num x 2
。 num
的遞歸調用。 具體來說,您有[... | ... a <- num xi, b <- num x (ni) ]
[... | ... a <- num xi, b <- num x (ni) ]
。 這將為a
每個值重新計算num x (ni)
。 您可以通過編寫[... | ... let b_input = num x (ni), a <- num xi, b <- b_input]
來將遞歸調用移出內部循環[... | ... let b_input = num x (ni), a <- num xi, b <- b_input]
[... | ... let b_input = num x (ni), a <- num xi, b <- b_input]
。 (編譯器優化可能會自動執行此操作,但您不應該依賴它。) 您應該保存並重復使用它們,而不是重新計算以前的結果。 最簡單的方法是在算法進行時保存先前結果的列表。 轉換代碼以保存以前的結果是一種稱為備忘錄的更通用技術的實例。
效率低下的另一個來源是num''
,它搜索整個列表以去除二次時間中的重復項。 可以使用Data.Set
模塊中的設置以n * log(n)時間刪除重復Data.Set
。
總之,在num kn
,請勿遞歸調用num
因為這會做多余的工作。 代替遞歸調用,保存num k 1
, num k 2
,... num k (n-1)
列表,並將此列表傳遞給num kn
。 另外,使用Data.Set
模塊刪除重復的值,而不是調用num''
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.