学习F# - 打印素数

时间:2009-07-08 11:12:27

标签: algorithm f# primes

昨天我在一些业余时间开始看F#。我想我会从标准问题开始,将所有素数打印到100以下。这就是我想出来的......

#light
open System

let mutable divisable = false
let mutable j = 2

for i = 2 to 100 do
    j <- 2
    while j < i do
        if i % j = 0 then divisable <- true
        j <- j + 1

    if divisable = false then Console.WriteLine(i)
    divisable <- false

事情是我觉得我从C / C#的角度来看这个并没有接受真正的功能语言方面。

我想知道其他人能想出什么 - 以及是否有人有任何提示/指示/建议。我感觉很好F#内容现在很难在网上找到,而我在5年前在大学时使用的最后一种功能语言是HOPE

7 个答案:

答案 0 :(得分:9)

以下是F#中的Sieve of Eratosthenes的简单实现:

let rec sieve = function
    | (p::xs) -> p :: sieve [ for x in xs do if x % p > 0 then yield x ]
    | []      -> []

let primes = sieve [2..50]
printfn "%A" primes  // [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47]

此实现不适用于非常大的列表,但它说明了功能解决方案的优雅。

答案 1 :(得分:3)

使用像Eratosthenes这样的Sieve函数是一个很好的方法。函数式语言在列表中运行得非常好,所以我首先考虑的是struture。

另一方面,功能语言的功能很好(heh)。对于函数式语言“感觉”,我将构建一个Sieve函数,然后调用它来打印出素数。你甚至可以拆分它 - 一个功能构建列表并完成所有工作,一个功能完成并完成所有打印,整齐地分离功能。

有几个有趣的版本here。 并且在其他类似语言中存在众所周知的实现。 OCAML中的Here's one在C中击败一个。

答案 2 :(得分:2)

你绝对不想从这个例子中学习,但我根据消息传递编写了a NewSqueak sieve的F#实现:

type 'a seqMsg =   
    | Die   
    | Next of AsyncReplyChannel<'a>   

type primes() =   
    let counter(init) =   
        MailboxProcessor.Start(fun inbox ->   
            let rec loop n =   
                async { let! msg = inbox.Receive()   
                        match msg with   
                        | Die -> return ()   
                        | Next(reply) ->   
                            reply.Reply(n)   
                            return! loop(n + 1) }   
            loop init)   

    let filter(c : MailboxProcessor<'a seqMsg>, pred) =   
        MailboxProcessor.Start(fun inbox ->   
            let rec loop() =   
                async {   
                    let! msg = inbox.Receive()   
                    match msg with   
                    | Die ->   
                        c.Post(Die)   
                        return()   
                    | Next(reply) ->   
                        let rec filter' n =   
                            if pred n then async { return n }   
                            else  
                                async {let! m = c.AsyncPostAndReply(Next)   
                                       return! filter' m }   
                        let! testItem = c.AsyncPostAndReply(Next)   
                        let! filteredItem = filter' testItem   
                        reply.Reply(filteredItem)   
                        return! loop()   
                }   
            loop()   
        )   

    let processor = MailboxProcessor.Start(fun inbox ->   
        let rec loop (oldFilter : MailboxProcessor<int seqMsg>) prime =   
            async {   
                let! msg = inbox.Receive()   
                match msg with   
                | Die ->   
                    oldFilter.Post(Die)   
                    return()   
                | Next(reply) ->   
                    reply.Reply(prime)   
                    let newFilter = filter(oldFilter, (fun x -> x % prime <> 0))   
                    let! newPrime = oldFilter.AsyncPostAndReply(Next)   
                    return! loop newFilter newPrime   
            }   
        loop (counter(3)) 2)   

    member this.Next() = processor.PostAndReply( (fun reply -> Next(reply)), timeout = 2000)

    interface System.IDisposable with
        member this.Dispose() = processor.Post(Die)

    static member upto max =   
        [ use p = new primes()
          let lastPrime = ref (p.Next())
          while !lastPrime <= max do
            yield !lastPrime
            lastPrime := p.Next() ]

有效吗?

> let p = new primes();;

val p : primes

> p.Next();;
val it : int = 2
> p.Next();;
val it : int = 3
> p.Next();;
val it : int = 5
> p.Next();;
val it : int = 7
> p.Next();;
val it : int = 11
> p.Next();;
val it : int = 13
> p.Next();;
val it : int = 17
> primes.upto 100;;
val it : int list
= [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71;
   73; 79; 83; 89; 97]

甜! :)

答案 3 :(得分:2)

这是我的两分钱:

let rec primes = 
  seq { 
    yield 2
    yield! (Seq.unfold (fun i -> Some(i, i + 2)) 3) 
           |> Seq.filter (fun p -> 
                primes
                |> Seq.takeWhile (fun i -> i * i <= p)
                |> Seq.forall (fun i -> p % i <> 0))
  }
  for i in primes do
    printf "%d " i

或许这个与isprime相同的更清晰版本被定义为一个单独的函数:

let rec isprime x =
    primes
    |> Seq.takeWhile (fun i -> i*i <= x)
    |> Seq.forall (fun i -> x%i <> 0)

and primes = 
    seq {
        yield 2
        yield! (Seq.unfold (fun i -> Some(i,i+2)) 3)
                |> Seq.filter isprime
    }

答案 4 :(得分:1)

简单但效率低下的建议:

  • 创建一个函数来测试单个数字是否为素数
  • 为2到100之间的数字创建一个列表
  • 按功能
  • 过滤列表
  • 使用其他功能撰写结果以打印结果

为了提高效率,你真的想通过检查它是否可以被任何较低的素数整除来测试一个素数,这将需要记忆。可能最好等到你的简单版本首先工作:)

请告诉我,如果这还不够暗示,我会想出一个完整的例子 - 认为它可能要到今晚......

答案 5 :(得分:1)

这里有my old post at HubFS关于使用递归seq来实现素数生成器。

对于您想要快速实施的情况,有nice OCaml code by Markus Mottl

P.S。如果你想要素数高达10 ^ 20,你真的想把D. J. Bernstein的Primegen移植到F#/ OCaml:)

答案 6 :(得分:1)

在解决同样的问题时,我在F#中实现了Sieve of Atkins。它是最有效的现代算法之一。

// Create sieve
let initSieve topCandidate =
    let result = Array.zeroCreate<bool> (topCandidate + 1)
    Array.set result 2 true
    Array.set result 3 true
    Array.set result 5 true
    result
// Remove squares of primes
let removeSquares sieve topCandidate =
    let squares =
        seq { 7 .. topCandidate}
            |> Seq.filter (fun n -> Array.get sieve n)
            |> Seq.map (fun n -> n * n)
            |> Seq.takeWhile (fun n -> n <= topCandidate)
    for n2 in squares do
        n2
            |> Seq.unfold (fun state -> Some(state, state + n2))
            |> Seq.takeWhile (fun x -> x <= topCandidate)
            |> Seq.iter (fun x -> Array.set sieve x false)
    sieve

// Pick the primes and return as an Array
let pickPrimes sieve =
    sieve
        |> Array.mapi (fun i t -> if t then Some i else None)
        |> Array.choose (fun t -> t)
// Flip solutions of the first equation
let doFirst sieve topCandidate =
    let set1 = Set.ofList [1; 13; 17; 29; 37; 41; 49; 53]
    let mutable x = 1
    let mutable y = 1
    let mutable go = true
    let mutable x2 = 4 * x * x
    while go do
        let n = x2 + y*y
        if n <= topCandidate then
            if Set.contains (n % 60) set1 then
                Array.get sieve n |> not |> Array.set sieve n

            y <- y + 2
        else
            y <- 1
            x <- x + 1
            x2 <- 4 * x * x
            if topCandidate < x2 + 1 then
                go <- false
// Flip solutions of the second equation
let doSecond sieve topCandidate =
    let set2 = Set.ofList [7; 19; 31; 43]
    let mutable x = 1
    let mutable y = 2
    let mutable go = true
    let mutable x2 = 3 * x * x
    while go do
        let n = x2 + y*y
        if n <= topCandidate then
            if Set.contains (n % 60) set2 then
                Array.get sieve n |> not |> Array.set sieve n

            y <- y + 2
        else
            y <- 2
            x <- x + 2
            x2 <- 3 * x * x
            if topCandidate < x2 + 4 then
                go <- false
// Flip solutions of the third equation
let doThird sieve topCandidate =
    let set3 = Set.ofList [11; 23; 47; 59]
    let mutable x = 2
    let mutable y = x - 1
    let mutable go = true
    let mutable x2 = 3 * x * x
    while go do
        let n = x2 - y*y
        if n <= topCandidate && 0 < y then
            if Set.contains (n % 60) set3 then
                Array.get sieve n |> not |> Array.set sieve n

            y <- y - 2
        else
            x <- x + 1
            y <- x - 1
            x2 <- 3 * x * x
            if topCandidate < x2 - y*y then
                go <- false

// Sieve of Atkin
let ListAtkin (topCandidate : int) =
    let sieve = initSieve topCandidate

    [async { doFirst sieve topCandidate }
     async { doSecond sieve topCandidate }
     async { doThird sieve topCandidate }]
        |> Async.Parallel
        |> Async.RunSynchronously
        |> ignore

    removeSquares sieve topCandidate |> pickPrimes

我知道有些人不建议使用Parallel Async,但它确实提高了我的2核心(超线程4个)i5的速度〜20%。这与我使用TPL的增长大致相同。

我尝试过以功能方式重写它,读取循环和可变变量,但性能下降了3-4倍,所以决定保留这个版本。