【问题标题】:Project Euler Problem 27 in F#F# 中的 Project Euler 问题 27
【发布时间】:2009-02-24 13:56:10
【问题描述】:

我一直在努力通过 Project Euler 的Problem 27 工作,但这似乎让我感到难过。首先,代码运行时间太长(在我的机器上可能需要几分钟,但更重要的是,它返回了错误的答案,尽管在浏览了一段时间后我真的无法发现算法有什么问题.

这是我当前的解决方案代码。

/// Checks number for primality.
let is_prime n = 
    [|1 .. 2 .. sqrt_int n|] |> Array.for_all (fun x -> n % x <> 0)

/// Memoizes a function.
let memoize f = 
    let cache = Dictionary<_, _>()
    fun x -> 
        let found, res = cache.TryGetValue(x)
        if found then
            res
        else
            let res = f x
            cache.[x] <- res
            res

/// Problem 27
/// Find a quadratic formula that produces the maximum number of primes for consecutive values of n.
let problem27 n =
    let is_prime_mem = memoize is_prime
    let range = [|-(n - 1) .. n - 1|]
    let natural_nums = Seq.init_infinite (fun i -> i)
    range |> Array.map (fun a -> (range |> Array.map (fun b ->
        let formula n = n * n + a * n + b
        let num_conseq_primes = natural_nums |> Seq.map (fun n -> (n, formula n))
                                |> Seq.find (fun (n, f) -> not (is_prime_mem f)) |> fst
        (a * b, num_conseq_primes)) |> Array.max_by snd)) |> Array.max_by snd |> fst

printn_any (problem27 1000)

关于如何 a) 让该算法实际返回正确答案的任何提示(我认为我至少采取了一种可行的方法)和 b) 提高​​性能,因为它显然超出了规定的“一分钟规则”在 Project Euler 常见问题解答中。我是函数式编程的新手,所以任何关于我如何考虑问题并考虑到更多函数式解决方案的建议也将不胜感激。

【问题讨论】:

  • 你有没有测试过“is_prime”函数来验证它确实产生了低于某个 N 的所有素数?

标签: f# functional-programming primes


【解决方案1】:

两个备注:

  1. 您可以利用b 必须是素数这一事实。这是因为问题要求n = 0, 1, 2, ... 的最长素数序列 所以,formula(0) 必须是以 开头的素数,但formula(0) = b,因此 b 必须是素数。

  2. 我不是 F# 程序员,但在我看来,代码根本没有尝试 n=0。这当然不符合问题的n必须从0开始的要求,因此产生正确答案的机会可以忽略不计。

【讨论】:

  • 谢谢 - 您的第一个提示大大提高了算法的速度!不知道我是怎么错过这么明显的东西的。关于您的第二点,我相信代码实际上确实尝试了 n = 0,因为函数 Seq.init_infinite 实际上在第一次迭代时将 0 作为参数传递。
  • 您是否测试过“is_prime”函数以验证它是否真的产生了低于某个 N 的所有素数?
  • 我已经在下面发布了我的完整解决方案,但是由于您的两个指针将我引向了正确的方向,所以答案是您的。再次感谢您。
【解决方案2】:

是的,经过大量检查所有辅助函数都在做他们应该做的事情后,我终于找到了一个可行的(并且相当有效的)解决方案。

首先,is_prime 函数是完全错误的(感谢 Dimitre Novatchev 让我看到它)。我不太确定我是如何得出我在原始问题中发布的函数的,但我认为它是有效的,因为我在以前的问题中使用过它。 (很可能,我刚刚对其进行了调整并破坏了它。)无论如何,这个函数的工作版本(对于所有小于 2 的整数至关重要地返回 false)是这样的:

/// Checks number for primality.
let is_prime n = 
    if n < 2 then false
    else [|2 .. sqrt_int n|] |> Array.for_all (fun x -> n % x <> 0)

main函数改成如下:

/// Problem 27
/// Find a quadratic formula that produces the maximum number of primes for consecutive values of n.
let problem27 n =
    let is_prime_mem = memoize is_prime
    let set_b = primes (int64 (n - 1)) |> List.to_array |> Array.map int
    let set_a = [|-(n - 1) .. n - 1|]
    let set_n = Seq.init_infinite (fun i -> i)
    set_b |> Array.map (fun b -> (set_a |> Array.map (fun a ->
        let formula n = n * n + a * n + b
        let num_conseq_primes = set_n |> Seq.find (fun n -> not (is_prime_mem (formula n)))
        (a * b, num_conseq_primes))
    |> Array.max_by snd)) |> Array.max_by snd |> fst

这里提高速度的关键是只为 b 的值生成 1 到 1000 之间的素数集(使用 primes 函数,我的实现埃拉托色尼筛法)。我还设法通过消除不必要的 Seq.map 使这段代码更加简洁。

所以,我对我现在的解决方案非常满意(只需不到一秒钟),当然任何进一步的建议仍然欢迎......

【讨论】:

    【解决方案3】:

    您可以使用概率算法来加速您的“is_prime”函数。最简单的快速算法之一是Miller-Rabin 算法。

    【讨论】:

    • 啊,这可能会有所帮助...如果没有其他明显的速度改进,我一定会试一试。谢谢。我仍然需要首先找到故障所在...
    • 至少使用 Erastothenes 的筛子。
    • @Harleqin:不幸的是,我不能使用埃拉托色尼筛法,因为我不知道可能生成的素数的上限(虽然我可以计算出一个安全限制,但它必须为每对系数完成)。无论如何,该算法现在似乎运行得足够快。
    • 您可以将 Erastothenes 的筛子实现为无限生成器。这个想法是,不是在某个范围内标记一个素数的倍数,而是将这个素数“放在”它的下一个倍数上。您保留一张表格,列出到目前为止的素数接下来会在哪里遇到。
    【解决方案4】:

    要摆脱一半的计算,您还可以使可能的数组只包含奇数

    【讨论】:

      【解决方案5】:

      我的超快速 python 解决方案:P

      flag = [0]*204
      primes = []
      
      def ifc(n): return flag[n>>6]&(1<<((n>>1)&31))
      
      def isc(n): flag[n>>6]|=(1<<((n>>1)&31))
      
      def sieve():
          for i in xrange(3, 114, 2):
              if ifc(i) == 0:
                  for j in xrange(i*i, 12996, i<<1): isc(j)
      
      def store():
          primes.append(2)
          for i in xrange(3, 1000, 2):
              if ifc(i) == 0: primes.append(i)
      
      def isprime(n):
          if n < 2: return 0
          if n == 2: return 1
          if n & 1 == 0: return 0
          if ifc(n) == 0: return 1
          return 0    
      
      def main():
          sieve()
          store()
          mmax, ret = 0, 0
          for b in primes:
              for a in xrange(-999, 1000, 2):
                  n = 1
                  while isprime(n*n + a*n + b): n += 1
                  if n > mmax: mmax, ret = n, a * b
          print ret
      
      main()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-17
        • 2010-09-27
        • 2010-09-17
        • 2010-09-27
        • 1970-01-01
        • 2011-10-11
        • 2019-07-26
        • 2011-03-23
        相关资源
        最近更新 更多