Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 在F中寻找素数非常慢#_Algorithm_F#_Primes - Fatal编程技术网

Algorithm 在F中寻找素数非常慢#

Algorithm 在F中寻找素数非常慢#,algorithm,f#,primes,Algorithm,F#,Primes,我已经用C语言中的埃拉托斯烯筛很容易地回答了欧拉计划的第7个问题,我对此没有任何问题 我对F#还是很陌生,所以我尝试了同样的技术 let prime_at pos = let rec loop f l = match f with | x::xs -> loop xs (l |> List.filter(fun i -> i % x <> 0 || i = x)) | _ -> l List.nth (loop [2..po

我已经用C语言中的埃拉托斯烯筛很容易地回答了欧拉计划的第7个问题,我对此没有任何问题

我对F#还是很陌生,所以我尝试了同样的技术

let prime_at pos =
  let rec loop f l =
    match f with 
    | x::xs -> loop xs (l |> List.filter(fun i -> i % x <> 0 || i = x))
    | _ -> l

  List.nth (loop [2..pos] [2..pos*pos]) (pos-1)
让prime_位于位置=
让rec循环fl=
匹配
|x::xs->loop xs(l |>List.filter(乐趣i->i%x0 | i=x))
|_ul->l
List.nth(循环[2..pos][2..pos*pos])(pos-1)
当pos<1000时,该功能运行良好,但在10000时会崩溃,不会出现内存不足异常

然后我尝试将算法更改为

let isPrime n = n > 1 && seq { for f in [2..n/2] do yield f } |> Seq.forall(fun i -> n % i <> 0)

seq {for i in 2..(10000 * 10000) do if isPrime i then yield i} |> Seq.nth 10000 |> Dump
让isPrime n=n>1&&seq{for f in[2..n/2]产生f}|>seq.forall(乐趣i->n%i0)
seq{for i in 2..(10000*10000)do if isPrime i然后产生i}|>seq.nth 10000 |>Dump
运行成功,但仍需要几分钟


如果我理解正确,第一个算法是尾部优化的,那么为什么会崩溃呢?我怎样才能写出一个在1分钟内运行的算法(我有一台速度很快的电脑)?

看看你的第一次尝试

let prime_at pos =
  let rec loop f l =
    match f with 
    | x::xs -> loop xs (l |> List.filter(fun i -> i % x <> 0 || i = x))
    | _ -> l

  List.nth (loop [2..pos] [2..pos*pos]) (pos-1)
在这里,问题非常相似,因为您正在为正在测试的每个元素生成巨大的列表


我在这里写了一个非常快速的F#sieve,它展示了如何更快地完成这项工作。

正如John已经提到的,您的实现很慢,因为它会生成一些临时数据结构

  • 在第一种情况下,您正在构建一个列表,该列表需要在内存中完全创建,这会带来很大的开销

  • 在第二种情况下,您正在构建一个惰性序列,它不消耗内存(因为它是在迭代时构建的),但它仍然引入了间接寻址,从而降低了算法的速度

在F#中的大多数情况下,人们倾向于选择可读性,因此使用序列是编写代码的一种很好的方法,但这里您可能更关心性能,所以我会避免使用序列。如果您想保持代码的相同结构,可以像这样重写
isPrime

let isPrime n = 
  let rec nonDivisible by =
    if by = 1 then true        // Return 'true' if we reached the end
    elif n%by = 0 then false   // Return 'false' if there is a divisor
    else nonDivisible (by - 1) // Otherwise continue looping

  n > 1 && nonDivisible (n/2)
当数字
n
不能被2到
n/2之间的任何数字整除时,这将用递归函数
不可分
替换序列和
forall
,该函数返回
true
。函数首先检查两种终止情况,否则执行递归调用


使用最初的实现,我能够在1.5秒内找到第1000个素数,而使用新的实现,需要22毫秒。在我的机器上,用新的实现找到10000次素数需要3.2秒。

请证明下降速度。这里10万次素数只需要5秒:我用这段代码在22毫秒内得到了第一个1000次素数。我真的对它的效果印象深刻!我理解这个算法,但是你能解释一下为什么它比序列快很多吗?我在某个地方读到,与使用状态机的C相比,F#引擎实现IEnumerable的能力很差。这可能是原因吗?@happygilmore:F#的当前版本也为序列表达式生成状态机;你读的东西一定已经过时了。@happygilmore你是说GordonBGoods答案的最后一段吗?@happygilmore:既然你在做Project Euler,对iPrime函数的另一个优化就是你只需要检查数字到目标的平方根。如果一个数的除数不小于或等于其平方根,则该数为素数。这明显低于n/2。
let isPrime n = 
  let rec nonDivisible by =
    if by = 1 then true        // Return 'true' if we reached the end
    elif n%by = 0 then false   // Return 'false' if there is a divisor
    else nonDivisible (by - 1) // Otherwise continue looping

  n > 1 && nonDivisible (n/2)