C#中的Euler 23:0.2秒,F#:30.5秒。 为什么?

我对这个问题的F#解决方案并不满意,因为我找不到一个漂亮而快速的解决方案,但这不是问题所在。 问题是,我将解决方案转换为C#以获得它,而且速度很快。 比如,非常快,相对而言。

我无法弄清楚为什么。 是的,我去过Reflector,C#代码看起来非常相似,不能说我真的了解IL但它看起来有点像。 我唯一能想到的是F#int []对C#List的表现。

所以这里:

F#

module Euler023 let divisorsSum n = let mutable sum = 1 let limit = (int (sqrt(float n))) for i in [2..limit] do if n%i=0 then sum x let abundants = [1..28123] |> List.filter isAbundant |> List.toArray let domain = System.Collections.BitArray(28124) let rec loopUntil ij = if i=abundants.Length then () elif j=abundants.Length then loopUntil (i+1) (i+1) else let sum = abundants.[i]+abundants.[j] if sum List.filter (fun x -> domain.Get(x)=false) |> List.sum 

C#

 static int divisorsSum(int n) { int sum = 0; var limit = (int)Math.Sqrt(n); for (int i=2;i<=limit;++i) if (n%i==0) sum += i + n/i; if ((limit * limit) == n) return sum-limit; return sum; } static List getAbundants(int ceiling) { var ret = new List(); for (int i = 1; i  i) ret.Add(i); return ret; } static void Main(string[] args) { var abundants = getAbundants(28124); var bitField = new bool[28124]; for (int i = 0; i < abundants.Count; ++i) for (int j = i; j < abundants.Count; ++j) { var sum = abundants[i] + abundants[j]; if (sum < 28124) bitField[sum] = true; else break; } var total = 0; for (int i = 0; i < 28124; ++i) if (bitField[i]==false) total += i; } 

更新

包含此代码的项目包含每个问题的单独文件(EulerXXX.fs)+主程序文件。 主程序文件如下

 module Program = let stopWatch = System.Diagnostics.Stopwatch() let mutable totalTime = System.TimeSpan() let inline tick() = stopWatch.Stop(); totalTime  Elapsed: %2.2f sec Total: %2.2fs" stopWatch.Elapsed.TotalSeconds totalTime.TotalSeconds stopWatch.Restart() let _ = stopWatch.Start() printf "Euler001 solution: %A" Euler001.solve tick() printf "Euler002 solution: %A" Euler002.solve tick() printf "Euler003 solution: %A" Euler003.solve tick() printf "Euler004 solution: %A" Euler004.solve tick() printf "Euler005 solution: %A" Euler005.solve tick() printf "Euler006 solution: %A" Euler006.solve tick() printf "Euler007 solution: %A" Euler007.solve tick() printf "Euler008 solution: %A" Euler008.solve tick() printf "Euler009 solution: %A" Euler009.solve tick() printf "Euler010 solution: %A" Euler010.solve tick() printf "Euler011 solution: %A" Euler011.solve tick() printf "Euler012 solution: %A" Euler012.solve tick() printf "Euler013 solution: %A" Euler013.solve tick() printf "Euler014 solution: %A" Euler014.solve tick() printf "Euler015 solution: %A" Euler015.solve tick() printf "Euler016 solution: %A" Euler016.solve tick() printf "Euler017 solution: %A" Euler017.solve tick() printf "Euler018 solution: %A" Euler018.solve tick() printf "Euler019 solution: %A" Euler019.solve tick() printf "Euler020 solution: %A" Euler020.solve tick() printf "Euler021 solution: %A" Euler021.solve tick() printf "Euler022 solution: %A" Euler022.solve tick() printf "Euler023 solution: %A" Euler023.solve tick() printf "Euler024 solution: %A" Euler024.solve tick() printf "Euler059 solution: %A" Euler059.solve tick() printf "Euler067 solution: %A" Euler067.solve tick() stopWatch.Stop() System.Console.ReadLine() 

该计划的产出如下:

 Euler001 solution: 233168 -> Elapsed: 0.02 sec Total: 0.02 s Euler002 solution: 4613732 -> Elapsed: 0.03 sec Total: 0.04 s ... Euler022 solution: 871198282 -> Elapsed: 0.02 sec Total: 4.11 s Euler023 solution: 4179871 -> Elapsed: 81.11 sec Total: 85.22 s Euler024 solution: [2; 7; 8; 3; 9; 1; 5; 4; 6; 0] -> Elapsed: 0.01 sec Total: 85.23 s ... Euler067 solution: [7273] -> Elapsed: 0.01 sec Total: 85.31 s 

所以问题不在于项目参数。 此外,如果我将代码从Euler023复制到程序,它将立即运行。 问题是,为什么这种减速只发生在这个问题上?

你的F#版本并不慢; 我的机器上的F#Interactive需要0.44秒。 我不知道你怎么能看到如此缓慢(30.5秒)。 如果您编译并运行代码,请确保您处于发布模式并启用优化和尾部调用消除。

但是,您仍然可以通过消除冗余中间集合的使用来进一步优化。

A.将(冗余)列表[2..limit] 2..limitdivisorsSum范围表达式2..limit

 for i in 2..limit do if n%i=0 then sum <- sum+i+n/i 

B.在不创建大列表的情况下生成丰富的数组(更忠实于C#版本):

 let abundants = let arr = ResizeArray(28123) for i in 1..28123 do if isAbundant i then arr.Add i arr.ToArray() 

C.计算solve而不创建大清单:

 let solve = loopUntil 0 0 let mutable sum = 0 for i in 1..28123 do if not <| domain.Get(i) then sum <- sum + i sum 

新的F#版本比原版快4倍; 完成需要大约0.1秒。

更新:

您的测量结果不准确。 首先,您测量了两次打印值调用之间的时间差。 其次, EulerXXX.solve是值; 所以在编译程序时会预先计算它们。 您应该将EulerXXX.solve声明为函数:

 let solve() = ... 

并测量函数调用的执行时间:

 let time fn = let sw = new System.Diagnostics.Stopwatch() sw.Start() let f = fn() sw.Stop() printfn "Time taken: %.2f s" <| (float sw.ElapsedMilliseconds)/1000.0 f let s023 = time Euler023.solve printf "Euler023 solution: %A" s023