F# 在F中生成素数时,为什么;“爱洛斯汀筛”;在这个特定的实现中如此缓慢?

F# 在F中生成素数时,为什么;“爱洛斯汀筛”;在这个特定的实现中如此缓慢?,f#,F#,即 我做错了什么?它是否必须与列表、序列和数组以及限制的工作方式有关 设置如下:我试图生成一些素数。我看到有10亿个素数的10亿个文本文件。问题不在于为什么……问题在于使用python的人是如何在毫秒内计算出1000000以下的所有素数的……我对下面的F#代码有什么错 在FSI中定时器打开的情况下,我获得了4.33秒的CPU时间,可获得100000。。。在那之后,一切都爆炸了 您的筛选功能很慢,因为您试图筛选出最多为top\u number的复合数字。使用Eratosthenes筛,您只需要这样

我做错了什么?它是否必须与列表、序列和数组以及限制的工作方式有关

设置如下:我试图生成一些素数。我看到有10亿个素数的10亿个文本文件。问题不在于为什么……问题在于使用python的人是如何在毫秒内计算出1000000以下的所有素数的……我对下面的F#代码有什么错


在FSI中定时器打开的情况下,我获得了4.33秒的CPU时间,可获得100000。。。在那之后,一切都爆炸了

您的筛选功能很慢,因为您试图筛选出最多为
top\u number
的复合数字。使用Eratosthenes筛,您只需要这样做,直到
sqrt(top_number)
和剩余的数本质上是素数。假设我们有
top\u number=1000000
,您的函数执行
78498
轮过滤(在
1000000
之前的素数),而原始筛选只执行
168次(在
1000
之前的素数)

可以避免生成偶数,但2不能从一开始就是素数。此外,
sieve
sieve\u prime
可以合并为递归函数。您可以使用轻量级的
列表。筛选
而不是
列表。选择

纳入上述建议:

let sieve_primes top_number = 
    let numbers = [ yield 2
                    for i in 3..2..top_number -> i ]
    let rec sieve ns = 
        match ns with
        | [] -> []
        | x::xs when x*x > top_number -> ns
        | x::xs -> x::sieve (List.filter(fun y -> y%x <> 0) xs)
    sieve numbers 
let sieve\u primes top\u number=
让数字=[产生2]
对于3..2..top_编号->i中的i]
设rec筛ns=
匹配
| [] -> []
|x::x x>top_编号->ns时的xs
|x::xs->x::sieve(List.filter(趣味y->y%x 0)xs)
筛号

在我的机器中,更新的版本非常快,根据我的代码,
top\u number=1000000

在0.6s内完成:stackoverflow.com/a/8371684/124259

获取fsi中22毫秒内的前100万个素数-此时可能正在编译代码

#time "on"

let limit = 1000000
//returns an array of all the primes up to limit
let table =
    let table = Array.create limit true //use bools in the table to save on memory
    let tlimit = int (sqrt (float limit)) //max test no for table, ints should be fine
    let mutable curfactor = 1;
    while curfactor < tlimit-2 do
        curfactor <- curfactor+2
        if table.[curfactor]  then //simple optimisation
            let mutable v = curfactor*2
            while v < limit do
                table.[v] <- false
                v <- v + curfactor
    let out = Array.create (100000) 0 //this needs to be greater than pi(limit)
    let mutable idx = 1
    out.[0]<-2
    let mutable curx=1
    while curx < limit-2 do
        curx <- curx + 2
        if table.[curx] then
            out.[idx]<-curx
            idx <- idx+1
    out
#时间“开启”
let limit=1000000
//返回一个数组,该数组包含最大值的所有素数
让桌子=
让table=Array.create limit true//使用表中的布尔值来节省内存
设tlimit=int(sqrt(float limit))//表的最大测试编号,int应该可以
设可变curfactor=1;
而curfactor
您已经实现了一个不同的算法,它遍历每个可能的值,并使用
%
确定是否需要删除它。你应该做的是用一个固定的增量来消除倍数。这将是渐进的


由于列表不支持随机访问,您无法有效地单步遍历列表,因此请使用数组。

对于使用列表(@pad)的常规试分算法和使用Eratosthenes(SoE)筛选数据结构的数组选择(@John Palmer和@Jon Harrop),都有一些很好的答案。然而,@pad的列表算法并不是特别快,并且会在更大的筛选范围内“爆炸”,而@John Palmer的数组解决方案稍微复杂一些,占用的内存比需要的多,并且使用外部可变状态,因此与使用命令式语言(如C#)编写的程序没有什么不同

EDIT\u ADD:我编辑了下面的代码(带有行注释的旧代码),修改了序列表达式,以避免一些函数调用,从而反映出更多的“迭代器风格”,虽然它节省了20%的速度,但仍然没有接近与“滚动您自己的枚举器”最终F#代码。我已相应地修改了下面的计时信息。END_EDIT

以下真正的SoE程序只使用64 KB的内存来筛选100万个素数(由于只考虑奇数和使用压缩位位数组),并且仍然几乎与@John Palmer的程序一样快,在大约40毫秒的时间内,在i7 2700K(3.5 GHz)上筛选100万个素数,只需几行代码:

open System.Collections
let primesSoE top_number=
  let BFLMT = int((top_number-3u)/2u) in let buf = BitArray(BFLMT+1,true)
  let SQRTLMT = (int(sqrt (double top_number))-3)/2
  let rec cullp i p = if i <= BFLMT then (buf.[i] <- false; cullp (i+p) p)
  for i = 0 to SQRTLMT do if buf.[i] then let p = i+i+3 in cullp (p*(i+1)+i) p
  seq { for i = -1 to BFLMT do if i<0 then yield 2u
                               elif buf.[i] then yield uint32(3+i+i) }
//  seq { yield 2u; yield! seq { 0..BFLMT } |> Seq.filter (fun i->buf.[i])
//                                          |> Seq.map (fun i->uint32 (i+i+3)) }
primesSOE 1000000u |> Seq.length;;
opensystem.Collections
让primesSoE成为最上面的数字=
让BFLMT=int((top_number-3u)/2u)在让buf=BitArray(BFLMT+1,true)中
设SQRTLMT=(int(sqrt(double-top_number))-3)/2
让rec cullp i p=如果i Seq.map(乐趣i->uint32(i+i+3))}
primesSOE 100000U |>序列长度;;
由于序列运行时库的低效性以及每次函数调用以大约28个时钟周期进行枚举以及每次迭代以大约16个函数调用返回的成本,几乎所有经过的时间都花费在最后两行中枚举找到的素数。这可以减少到每次只调用几个函数通过滚动我们自己的迭代器来实现,但代码并不简洁;请注意,在以下代码中,除了筛选数组的内容和使用对象表达式实现迭代器所需的引用变量外,没有任何可变状态公开:

open System
open System.Collections
open System.Collections.Generic
let primesSoE top_number=
  let BFLMT = int((top_number-3u)/2u) in let buf = BitArray(BFLMT+1,true)
  let SQRTLMT = (int(sqrt (double top_number))-3)/2
  let rec cullp i p = if i <= BFLMT then (buf.[i] <- false; cullp (i+p) p)
  for i = 0 to SQRTLMT do if buf.[i] then let p = i+i+3 in cullp (p*(i+1)+i) p
  let nmrtr() =
    let i = ref -2
    let rec nxti() = i:=!i+1;if !i<=BFLMT && not buf.[!i] then nxti() else !i<=BFLMT
    let inline curr() = if !i<0 then (if !i= -1 then 2u else failwith "Enumeration not started!!!")
                        else let v = uint32 !i in v+v+3u
    { new IEnumerator<_> with
        member this.Current = curr()
      interface IEnumerator with
        member this.Current = box (curr())
        member this.MoveNext() = if !i< -1 then i:=!i+1;true else nxti()
        member this.Reset() = failwith "IEnumerator.Reset() not implemented!!!"
      interface IDisposable with
        member this.Dispose() = () }
  { new IEnumerable<_> with
      member this.GetEnumerator() = nmrtr()
    interface IEnumerable with
      member this.GetEnumerator() = nmrtr() :> IEnumerator }
primesSOE 1000000u |> Seq.length;;
开放系统
开放系统。集合
open System.Collections.Generic
让primesSoE成为最上面的数字=
让BFLMT=int((top_number-3u)/2u)在让buf=BitArray(BFLMT+1,true)中
设SQRTLMT=(int(sqrt(double-top_number))-3)/2

let rec culp i p=如果我的标准建议是使用数组而不是列表/seqs-要想快速筛选,请看这里:我不得不说
List。choose
是这里的主要性能杀手,但你在这里有这么多的对象创建,这一点都不好笑。好吧,geeze。这有点好笑:D在我看来,我只是在编程赢了为几个人买东西months@cdonlan好吧,主要的问题是你没有正确地实现算法。如果你使用数组而不是列表,这会更快。好吧,看起来不错。没有意识到这取决于
open System
open System.Collections
open System.Collections.Generic
let primesSoE top_number=
  let BFLMT = int((top_number-3u)/2u) in let buf = BitArray(BFLMT+1,true)
  let SQRTLMT = (int(sqrt (double top_number))-3)/2
  let rec cullp i p = if i <= BFLMT then (buf.[i] <- false; cullp (i+p) p)
  for i = 0 to SQRTLMT do if buf.[i] then let p = i+i+3 in cullp (p*(i+1)+i) p
  let nmrtr() =
    let i = ref -2
    let rec nxti() = i:=!i+1;if !i<=BFLMT && not buf.[!i] then nxti() else !i<=BFLMT
    let inline curr() = if !i<0 then (if !i= -1 then 2u else failwith "Enumeration not started!!!")
                        else let v = uint32 !i in v+v+3u
    { new IEnumerator<_> with
        member this.Current = curr()
      interface IEnumerator with
        member this.Current = box (curr())
        member this.MoveNext() = if !i< -1 then i:=!i+1;true else nxti()
        member this.Reset() = failwith "IEnumerator.Reset() not implemented!!!"
      interface IDisposable with
        member this.Dispose() = () }
  { new IEnumerable<_> with
      member this.GetEnumerator() = nmrtr()
    interface IEnumerable with
      member this.GetEnumerator() = nmrtr() :> IEnumerator }
primesSOE 1000000u |> Seq.length;;