Daniel Wagner's answer解釋了爲運行時複雜性推導界限的一般策略。但是,通常情況下,一般策略的情況下,它會產生過於保守的界限。
因此,我們來仔細研究一下這個例子。
main = print (head (filter isPrime (filter ((==0) . (n `mod`)) [n-1,n-2..])))
where n = 600851475143
(旁白:如果n
是黃金,這將檢查n `mod` 0 == 0
時導致運行時錯誤,因此我的列表改變爲[n, n-1 .. 2]
以便算法適用於所有n > 1
)
讓我們分手了表達到它的部分,所以我們可以看到各部分更容易地分析
main = print answer
where
n = 600851475143
candidates = [n, n-1 .. 2]
divisorsOfN = filter ((== 0) . (n `mod`)) candidates
primeDivisors = filter isPrime divisorsOfN
answer = head primeDivisors
像丹尼爾,我與算術運算,比較等都是O(1)假設工作 - 作爲n不錯,對於所有遠程合理的輸入來說,這是一個足夠好的近似值。
因此,列表candidates
,都將產生從n
元件向下answer
,n - answer + 1
元件,對於一個O(n - answer + 1)
總成本。對於複合n
,我們有answer <= n/2
,那麼這就是Θ(n)。
根據需要生成除數列表然後Θ(n - answer + 1)
也是。
對於n
的因數d(n)
,我們可以使用粗略估計值d(n) <= 2√n
。
所有因數>= answer
的n
必須檢查素數,這是所有除數的至少一半。 由於被懶惰地產生除數的列表,所述的
isPrime :: Integer -> Bool
isPrime p = (divisors p) == [1, p]
複雜度爲O(P的最小素因子),因爲一旦第一除數> 1
被找到時,平等測試被確定。對於複合p
,最小的素因子是<= √p
。
我們有< 2√n
最壞情況下O(√n)的複雜性的素數檢查,以及一個複雜的檢查Θ(answer)
,所以所有進行的主要測試的組合工作是O(n)。
總結起來,所需的總工作量爲O(n)
,因爲每個步驟的成本最差爲O(n)
。
事實上,在這個算法中完成的全部工作是Θ(n)
。如果n
是素數,則根據需要生成除數列表在O(1)中完成,但主要測試是Θ(n)
。如果n
是複合的,則answer <= n/2
,並且根據需要生成除數列表是Θ(n)
。
如果我們不考慮算術運算爲O(1),我們必須乘以算術運算的複雜度,數字大小爲n
,即O(log n)
位,這取決於所使用的算法通常給出略高於log n
和低於(log n)^2
的因子。
對代碼分析/優化的一個很好的參考在這裏:http://book.realworldhaskell.org/read/profiling-and-optimization.html。但是,在深入研究之前,我會重新考慮您使用的算法。 – jtobin 2012-07-26 00:51:11
@jtobin通過「運行效率」,我正在使用大O符號在理論層面上進行思考。我可以用有針對性的語言進行這種類型的分析。我對函數式語言很陌生,所以這種類型的分析對我來說很棘手。 「重新思考我的算法」正是我所想到的。 – 2012-07-26 00:54:04
所有答案的淨問題是,他們沒有得到任何這樣的強力代碼來做素數因子分解的結果,儘管它可能代表了一種「計算方法」,它將得到一個解決方案......它不會做所以在任何合理的時間和功耗。這不是一個「代碼」問題,它是「我該如何開發一個更好的算法或者」近似算法「(通常可以更快速地產生答案,但可能不會產生答案)參見關於素數因子分解的研究.. – ErstwhileIII 2014-09-09 11:06:18