나는 처음에 답을 쓸 계획이 아니었다. 그러나 다른 사용자가 첫 커플 프라임을 곱하면 계산 비용이 많이 든다는 점을 반복해서 적용한다는 이상한 주장을들은 후에 나는 들었다 lcm
. 두 알고리즘과 벤치 마크는 다음과 같습니다.
내 알고리즘 :
프라임 생성 알고리즘으로 무한의 프라임 목록을 제공합니다.
isPrime :: Int -> Bool
isPrime 1 = False
isPrime n = all ((/= 0) . mod n) (takeWhile ((<= n) . (^ 2)) primes)
toPrime :: Int -> Int
toPrime n
| isPrime n = n
| otherwise = toPrime (n + 1)
primes :: [Int]
primes = 2 : map (toPrime . (+ 1)) primes
이제 소수 목록을 사용하여 일부 결과를 계산하십시오 N
.
solvePrime :: Integer -> Integer
solvePrime n = foldl' (*) 1 $ takeWhile (<= n) (fromIntegral <$> primes)
이제 다른 lcm 기반 알고리즘은 상당히 간결합니다. 주로 처음부터 프라임 생성을 구현했기 때문에 (그리고 성능이 좋지 않기 때문에 슈퍼 간결한 목록 이해 알고리즘을 사용하지 않았기 때문에) lcm
단순히에서 가져 왔습니다 Prelude
.
solveLcm :: Integer -> Integer
solveLcm n = foldl' (flip lcm) 1 [2 .. n]
-- Much slower without `flip` on `lcm`
이제 벤치 마크의 경우 각각에 사용 된 코드는 간단했습니다. ( -prof -fprof-auto -O2
then +RTS -p
)
main :: IO ()
main = print $ solvePrime n
-- OR
main = print $ solveLcm n
를 들어 n = 100,000
, solvePrime
:
total time = 0.04 secs
total alloc = 108,327,328 bytes
vs solveLcm
:
total time = 0.12 secs
total alloc = 117,842,152 bytes
를 들어 n = 1,000,000
, solvePrime
:
total time = 1.21 secs
total alloc = 8,846,768,456 bytes
vs solveLcm
:
total time = 9.10 secs
total alloc = 8,963,508,416 bytes
를 들어 n = 3,000,000
, solvePrime
:
total time = 8.99 secs
total alloc = 74,790,070,088 bytes
vs solveLcm
:
total time = 86.42 secs
total alloc = 75,145,302,416 bytes
나는 그 결과가 스스로를 말한다고 생각한다.
프로파일 러는 프라임 생성이 작을수록 실행 시간의 비율이 점점 작아짐을 나타냅니다 n
. 따라서 병목 현상이 아니므로 지금은 무시할 수 있습니다.
이것은 우리가 lcm
한 인자가 1에서으로가는 곳과 다른 인자가 1에서으로가는 곳을 부르는 것을 실제로 비교하고 있음을 의미 n
합니다 ans
. *
동일한 상황에서 전화를 걸고 프라임이 아닌 모든 번호를 건너 뛰는 이점이 *
있습니다.
그리고 잘 알려져있다 *
빠르고보다 lcm
같이 lcm
반복 응용 프로그램을 필요로 mod
하고, mod
점근 적으로 느린이다 ( O(n^2)
대 ~O(n^1.5)
).
따라서 위의 결과와 간단한 알고리즘 분석을 통해 어떤 알고리즘이 더 빠른지 알 수 있습니다.