F# 中的项目欧拉问题 27
-
06-09-2019 - |
题
我一直在努力解决问题 问题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)提高性能的任何提示,因为它明显超出了项目中规定的“一分钟规则”欧拉常见问题解答。我是函数式编程的新手,因此任何有关我如何考虑问题并考虑到更函数式解决方案的建议也将不胜感激。
解决方案
两点备注:
您可以利用以下事实:
b
必须是素数. 。这是因为该问题要求最长的素数序列n = 0, 1, 2, ...
所以,formula(0)
必须首先是质数,但是formula(0) = b
, ,因此,b 必定是素数。我不是 F# 程序员,但是 在我看来,代码根本没有尝试 n= 0. 。这当然不能满足问题的要求
n
必须从0
, ,因此产生正确答案的机会可以忽略不计。
其他提示
右键,很多检查,所有的辅助功能正在做什么,他们应该后,我终于达到了一个工作(和合理高效的)解决方案。
首先, is_prime 的功能是完全错误的(感谢Dimitre Novatchev让我看看那个)。我不知道我很是如何得到我张贴在原来的问题的功能,但我认为这是工作,因为我已经在前面的问题,用它。 (最有可能的,我刚刚调整了它,因为它弄坏了。)无论如何,该功能的工作版本(其严重返回小于2的所有整数假)是这样的:
/// 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)
主要功能改变为以下内容:
/// 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
这里,以增加速度的关键是仅生成的值在1和1000之间的一组素数的 B'/ em>的(使用素数功能,我的实现的埃拉托塞尼方法的筛)。我也设法使此代码稍微更简洁通过消除不必要Seq.map。
所以,我对这个解决办法,我现在很高兴(它只需在第二),但当然,任何进一步的建议,仍然会受到欢迎...
您可以通过使用一个概率算法加快你的“is_prime”功能。一种最简单的快速算法是这种米勒罗宾算法
摆脱一半的计算量,你也可以成为可能a's的阵列只包含奇数
我的超快蟒溶液: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()