C# 为什么F代码比C代码慢?
我正在再次处理Euler项目的问题(在我学习C#时做过前23个问题),我对我的问题5解决方案表现不佳感到困惑 内容如下: 2520是可以被每个数字除的最小数字 从1到10,没有任何余数 能被全部整除的最小正数是多少 从1到20的数字是多少 现在,我难以置信的原始蛮力解决方案C#在大约25秒内解决了这个问题C# 为什么F代码比C代码慢?,c#,f#,C#,F#,我正在再次处理Euler项目的问题(在我学习C#时做过前23个问题),我对我的问题5解决方案表现不佳感到困惑 内容如下: 2520是可以被每个数字除的最小数字 从1到10,没有任何余数 能被全部整除的最小正数是多少 从1到20的数字是多少 现在,我难以置信的原始蛮力解决方案C#在大约25秒内解决了这个问题 var numbers = Enumerable.Range(1, 20); int start = 1; int result; while (true) { if (numbers.
var numbers = Enumerable.Range(1, 20);
int start = 1;
int result;
while (true)
{
if (numbers.All(n => start % n == 0))
{
result = start;
break;
}
start++;
}
现在,我的F#解决方案也使用了暴力强迫,但至少它有更多的辨别力,所以在我看来,它“应该”运行得更快,但它以45秒左右的时钟停止,所以它的速度几乎是C#1的两倍
我知道我的解决方案可能会更好,在这种情况下,我只是想知道,一个更好的暴力解决方案怎么会比原始解决方案慢这么多。好吧,一定是这一点
divisors |> Seq.takeWhile(fun x -> n % x = 0)
Seq.length dividesBy = Seq.length divisors
我认为您可以将其重写为一个更简单的递归函数,它将更类似于您最初的c#实现 您可以使用
List.forall
而不是转换为seq,然后执行seq.length
:
let divisors = [3..20] |> List.rev
let isDivOneToTwenty n = divisors |> List.forall (fun d -> n % d = 0)
Seq.length
将需要遍历整个序列以确定元素的数量,而forall
可以在元素未通过谓词时立即返回
您还可以将findNum
编写为:
let rec findNum n = if isDivOneToTwenty n then n else findNum (n + 2)
甚至更直接的翻译,如
let numbers = { 1..20 }
let rec loop start =
if numbers |> Seq.forall (fun n -> start % n = 0)
then start
else loop (start + 1)
loop 1
需要一分半钟(你的C#版本在我的机器上也需要25秒)。数字序列似乎是罪魁祸首,因为它将其更改为数组([|1..20 |]
)并使用数组。因为所有的都会将其降低到8秒。使用数组的C#版本需要20秒(使用我自己的数组专用的ForAll
方法而不是Enumerable.All
需要17秒)
编辑:在看到李的答案后,我尝试了列表。对于所有的,它甚至比数组快(~5秒)。就是它。实际上,在我的测试中,List.forall
与Seq.forall
相比有很大的不同。就像100秒对10秒。@mikez-YestakeWhile
会提前中断,每次都会将该计数与整个除数序列的长度进行比较,因此每次调用时它都会遍历一到两次。是的,我在写完我的评论后就意识到了这一点,所以我删除了它。+1。这个简单的更改将我的机器的运行时间减少到2,5秒左右。这正是我想要的。请注意,解决方案也必须是2520的倍数。因此,您的增量可以是2520,您将测试的数字要少得多。即使有一个简单的暴力解决方案,它也会很快实现。另外,你不需要测试1到10的可除性。这不是一个完全相同的。但这也解释了为什么F#现在真的比C#@L.B慢?你要来这里对演出说粗话吗?我从未声称这是一个特别好的解决方案。不是每个人天生都有很好的编码天赋,也许这就是我为什么要学习的原因?
let numbers = { 1..20 }
let rec loop start =
if numbers |> Seq.forall (fun n -> start % n = 0)
then start
else loop (start + 1)
loop 1