我对这个问题的F#解决方案并不满意,因为我找不到一个漂亮的&快速解决方案,但这不是问题。问题是,我将解决方案转换为C#,因为它很快。喜欢,非常快,相对而言。
我无法弄清楚原因。是的,我去过Reflector,C#代码看起来非常相似,不能说我真的了解IL但它看起来有点像。我唯一能想到的是F#int []对C#List的表现。
所以这就是:
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 <- sum+i+n/i
if (limit*limit)=n then sum-limit else sum
let isAbundant x = (divisorsSum x)>x
let abundants = [1..28123] |> List.filter isAbundant |> List.toArray
let domain = System.Collections.BitArray(28124)
let rec loopUntil i j =
if i=abundants.Length then ()
elif j=abundants.Length then loopUntil (i+1) (i+1)
else
let sum = abundants.[i]+abundants.[j]
if sum<28124 then
domain.Set(sum, true)
loopUntil i (j+1)
else
loopUntil (i+1) (i+1)
let solve = loopUntil 0 0
[1..28123] |> List.filter (fun x -> domain.Get(x)=false) |> List.sum
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<int> getAbundants(int ceiling)
{
var ret = new List<int>();
for (int i = 1; i < ceiling; ++i) if (divisorsSum(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 <- totalTime.Add stopWatch.Elapsed
printfn " -> Elapsed: %2.2f sec Total: %2.2f s" 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复制到程序,它将立即运行。 问题是,为什么这种减速只发生在这个问题上?
答案 0 :(得分:5)
你的F#版本并不慢;我的机器上的F#Interactive需要0.44秒。我不知道你怎么能看到这么慢(30.5秒)。如果您编译并运行代码,请确保您处于发布模式并启用优化和尾部调用消除。
但是,您仍然可以通过消除使用冗余中间集合来进一步优化。
一个。将{冗余)列表[2..limit]
更改为2..limit
中的范围divisorsSum
:
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()
℃。在不创建大清单的情况下计算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