我正在使用F#进行实验,并使用FSI REPL。我注意到,在我的初学者的朴素埃拉托色尼筛法实现中,两个略有不同的实现之间存在巨大的效率差异。第一个实现增加了一个if语句:
let rec sieve max current pList =
match current with
| 2 -> sieve max (current + 1) (current::pList)
| 3 -> sieve max (current + 2) (current::pList)
| n when n < max ->
if (n % 5 = 0) || (n % 3 = 0) then
sieve max (current + 2) (current::pList)
else if (pList |> (List.map (fun n -> current % n = 0)) |> List.contains true) then
sieve max (current + 2) (pList)
else
sieve max (current + 2) (current::pList)
| n when n >= max
-> pList
| _
-> printfn "Error: list length: %A, current: %A" (List.length pList) current
[-1]
第一个包含 HTML 标记,第二个不包含:
let rec sieve max current pList =
match current with
| 2 -> sieve max (current + 1) (current::pList)
| 3 -> sieve max (current + 2) (current::pList)
| n when n < max ->
if (pList |> (List.map (fun n -> current % n = 0)) |> List.contains true) then
sieve max (current + 2) (pList)
else
sieve max (current + 2) (current::pList)
| n when n >= max
-> pList
| _
-> printfn "Error: list length: %A, current: %A" (List.length pList) current
[-1]
拥有额外
if
分支的实现实际上速度较慢,尽管看起来应该更快。
我在REPL中使用以下命令计时了这两种实现:
#time
sieve 200000 2 []
#time
并且发现在我的机器上,具有额外if
语句的实现需要大约2分钟,而没有if
语句的实现每次运行大约需要1分钟。
这是如何可能的?通过添加一个负责3或5的倍数的if
语句,它实际上比仅映射整个列表,然后查找是否存在质数列表中的数字除数要慢。
为什么?只是因为F#针对处理列表进行了优化吗?