簡體   English   中英

通過遞歸檢查尾部來迭代列表的性能

[英]Performance of iterating a list by recursively examining the tail

我決定嘗試通過做一些CodinGame挑戰來學習Haskell(所以這個問題是超級初學者級的東西,我敢肯定)。 其中一個需要在整數列表中搜索任意兩個值之間的最小差異。 我以前通過這樣做在Clojure中解決了它:

(ns Solution
  (:gen-class))

(defn smallest-difference [values]
  (let [v (sort values)]
    (loop [[h & t] v curr-min 999999]
      (if (nil? t) curr-min
        (let [dif (- (first t) h)]
          (recur t (if (> curr-min dif) dif curr-min)))))))

(defn -main [& args]
  (let [horse-strengths (repeatedly (read) #(read))]
      (let [answer (smallest-difference horse-strengths)]
        (println answer)))) 

我嘗試在Haskell中實現相同的解決方案,具體如下:

readHorses :: Int -> [Int] -> IO [Int]
readHorses n h
    | n < 1 = return h
    | otherwise = do
        l <- getLine
        let hn = read l :: Int
        readHorses (n - 1) (hn:h)

findMinDiff :: [Int] -> Int -> Int
findMinDiff h m
    | (length h) < 2    = m
    | (h!!1 - h!!0) < m = findMinDiff (tail h) (h!!1 - h!!0)
    | otherwise         = findMinDiff (tail h) m



main :: IO ()
main = do
    hSetBuffering stdout NoBuffering -- DO NOT REMOVE
    input_line <- getLine
    let n = read input_line :: Int
    hPrint stderr n
    horses <- readHorses n []
    hPrint stderr "Read all horses"
    print (findMinDiff (sort horses) 999999999)
    return ()

對於Clojure解決方案沒有的大輸入(99999值),這次超時。 然而,它們看起來與我很相似。

至少從表面上看,讀取值並構建列表不是問題,因為在超時之前會打印“Read all horses”。

如何使Haskell版本更高效?

您在每次遞歸中計算列表的lengthfindMinDiff 由於length需要O(n)時間,因此findMinDiff需要O(n^2)時間而不是O(n)

您可以使用模式匹配而不是length編寫相同的東西, !! tail

findMinDiff :: [Int] -> Int -> Int
findMinDiff (h0 : hs@(h1 : _)) m = 
    if h1 - h0 < m
    then findMinDiff hs (h1 - h0)
    else findMinDiff hs m
findMinDiff _ m = m

順便說一下,完全替代的實現可以寫成如下。 (偽代碼如下)

拿這份清單

h = [h0, h1, h2 ...

刪除一個元素

drop 1 h = [h1, h2, h3 ...

計算逐點差異

zipWith (-) (drop 1 h) h = [h1-h0, h2-h1, h3-h2, ...

然后采取最低限度。 完整代碼:

minDiff :: [Int] -> Int
minDiff h = minimum (zipWith (-) (drop 1 h) h)

請注意,這將在空列表中崩潰。 另一方面,沒有必要使用9999999 hack。 由於懶惰,它也在恆定的空間中運行。

為了更好的錯誤處理:

minDiff :: [Int] -> Int
minDiff [] = error "minDiff: empty list"
minDiff h = minimum (zipWith (-) (drop 1 h) h)

甚至,更迂腐(但尊重整體):

minDiff :: [Int] -> Maybe Int
minDiff [] = Nothing
minDiff h  = Just (minimum (zipWith (-) (drop 1 h) h))

暫無
暫無

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

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