【问题标题】:Learning F# - printing prime numbers学习 F# - 打印素数
【发布时间】:2010-11-09 00:06:44
【问题描述】:

昨天我在一些空闲时间开始研究 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

【问题讨论】:

  • 附带说明,使用Console.WriteLine 非常不符合 F# 的精神。我建议改用printfn "%i" i
  • printf 更安全,可以推断出一些类型。
  • 尝试使用折叠重写它。

标签: algorithm f# primes


【解决方案1】:

简单但低效的建议:

  • 创建一个函数来测试单个数字是否为素数
  • 为 2 到 100 的数字创建一个列表
  • 按函数过滤列表
  • 使用另一个函数组合结果以打印出结果

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

如果这还不够暗示,请告诉我,我会想出一个完整的例子——我想可能要到今晚才知道......

【讨论】:

【解决方案2】:

使用像 Eratosthenes 这样的 Sieve 函数是一个不错的方法。函数式语言与列表配合得非常好,所以我会从结构上考虑到这一点。

另一方面,函数式语言可以很好地由函数构成(呵呵)。对于函数式语言“感觉”,我会构建一个 Sieve 函数,然后调用它来打印素数。您甚至可以将其拆分 - 一个函数构建列表并完成所有工作,而一个函数通过并完成所有打印,巧妙地分离功能。

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

【讨论】:

    【解决方案3】:

    这是Sieve of Eratosthenes 在 F# 中的简单实现:

    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]
    

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

    【讨论】:

    • 很好,但我认为“List.filter (fun x -> x % p > 0) xs”会比显式列表理解更惯用。
    • 请记住,这不是真正的筛子。该算法非常慢(与真正的筛子相比,渐近复杂度差)。
    • 让我解释一下区别。埃拉托色尼筛法只标记当前素数的倍数(代码中的p)。所以这是当前素数步数的倍数。但是,您的代码对所有剩余数字执行可分性测试,而不仅仅是倍数。随着数字变大,非倍数比倍数多得多,因此与真正的筛子相比,您的算法做了很多额外的工作。
    • 这个想法来自这篇很棒的论文:cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
    • 感谢您的信息,非常有趣!这段代码只是上面链接中的 Haskell 示例的一个端口。
    【解决方案4】:

    你肯定不想学这个例子,但是我写了一个基于消息传递的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]
    

    甜! :)

    【讨论】:

    • 对于每天都在做的人来说,这里有一些简单的代码:)。可能会传递您的示例,但是谢谢大声笑...
    【解决方案5】:

    这是my old post at HubFS 关于使用递归序列实现素数生成器的内容。

    如果你想快速实施,有nice OCaml code by Markus Mottl

    附:如果您想将质数迭代到 10^20,您真的想将 D. J. Bernstein 的 primegen 移植到 F#/OCaml :)

    【讨论】:

      【解决方案6】:

      在解决同样的问题时,我在 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 倍,因此决定保留此版本。

      【讨论】:

        【解决方案7】:

        这是我的两分钱:

        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
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-09-27
          • 2015-12-24
          • 2011-03-16
          • 1970-01-01
          • 1970-01-01
          • 2018-10-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多