簡體   English   中英

如何在Haskell中優化此代碼

[英]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 1num k 1組合而生成的所有數字的集合, num k 3是通過組合num k 1num k 2生成的所有數字,依此類推。 每個步驟都使用先前步驟中計算出的集合,並應用一個運算符。 這是前3個步驟。 請注意,每個數字都是使用兩個先前生成的數字和一個運算符計算的。

  1. num 3 1 = {3}
  2. num 3 2 = {3-3 = 0,3/3 = 1,3 = 3,3 + 3 = 6,3 * 3 = 9}
  3. 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 1num x 2num x 3 然后, num x 3將再次計算num x 1num 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 1num k 2 ,... num k (n-1)列表,並將此列表傳遞給num kn 另外,使用Data.Set模塊刪除重復的值,而不是調用num''

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM