ダニエル・ワグナーの答えは、ランタイムの複雑さの境界を導き出す一般的な戦略をかなりうまく説明しています。ただし、一般的な戦略の場合によくあることですが、あまりにも控えめな境界が生成されます。
せっかくなので、この例をもう少し詳しく調べてみましょう。
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) であるという仮定で作業します。真実ではありませんが、それはすべてのリモートで妥当な入力の十分な近似値です。
したがって、リストのうち、下candidates
からn
下の要素answer
を生成する必要があります。n - answer + 1
要素の合計コストはO(n - answer + 1)
です。複合n
の場合、 がありanswer <= n/2
、それが Θ(n) です。
必要に応じて除数のリストを生成することΘ(n - answer + 1)
も同様です。
d(n)
の約数については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) であり、複雑さのチェックは 1 つΘ(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