Javascript 使用ProjectEuler2编写缓慢的Haskell和Python代码
我和Haskell一起练习了一段时间,以了解这门语言,它太神奇了,所以我去了euler项目,做了第二题,花了相当长的时间~30-40秒,我不知道到底要完成多长时间。我想知道为什么花了这么长时间,所以我用F、C Javascript和Python尝试了同样的方法。F和C与javascript一样需要几毫秒才能完成,但python比Haskell花的时间要多。为什么呢?这些是我的实现 哈斯克尔 fib 0=1 fib 1=1 fibn=fibn-1+fibn-2 genFibs n最大值= 如果fib nJavascript 使用ProjectEuler2编写缓慢的Haskell和Python代码,javascript,c#,python,haskell,f#,Javascript,C#,Python,Haskell,F#,我和Haskell一起练习了一段时间,以了解这门语言,它太神奇了,所以我去了euler项目,做了第二题,花了相当长的时间~30-40秒,我不知道到底要完成多长时间。我想知道为什么花了这么长时间,所以我用F、C Javascript和Python尝试了同样的方法。F和C与javascript一样需要几毫秒才能完成,但python比Haskell花的时间要多。为什么呢?这些是我的实现 哈斯克尔 fib 0=1 fib 1=1 fibn=fibn-1+fibn-2 genFibs n最大值= 如果fi
(function () {
function fib(n) {
return n < 2 ? 1 : fib(n - 1) + fib(n - 2);
}
var totalSum = 0;
var num = 1;
var n = 1;
while(num < 4000000)
{
num = fib(n);
if (num % 2 == 0) totalSum += num;
n += 1;
}
alert(totalSum);
})();
那么,有人能解释为什么Haskell和Python在这方面很慢,而F和C在这方面很快,以及我如何增强它吗?任何帮助都将不胜感激
编辑:Haskell代码已更正,Python更好地使用备忘录实现尝试将其备忘录化
known_fibs={}
def fib(n):
if n not in known_fibs:
known_fibs[n] = 1 if n < 2 else fib(n-1) + fib(n-2)
return known_fibs[n]
你想知道所有的时间都在哪里,对吗?不要把它看作是测量时间。可以将其视为查找大部分时间都在堆栈上的代码行,而不考虑总时间。下面是一个例子:
许多探查器落入gprof陷阱,包括忽略阻塞时间、认为代码行无关紧要、认为自我时间无关紧要,以及认为测量必须准确。Haskell实现完全错误:它永远不会完成,因为: fibn=fibn-1+fibn-2 同: fibn=fibn-1+fibn-2 这是有分歧的 正确的实施: fib 0=1 fib 1=1 fibn=fibn-1+fibn-2 genFibs n maxVal |fib n
我对F和C的速度的猜测是编译器引入了某种形式的记忆以避免指数增长。尽管问题可能仍然太小,无法注意到函数调用的指数增长。如果你试图将4000000增加到4000000或更多,你肯定可以检查这是否正确。斐波那契函数的递归定义是解决这个问题的糟糕方法。下面是一个JavaScript实现,它基本上是即时运行的:
function evenFibSum(limit) {
var
pf, f, t, sum;
for (pf = 1, f = 2, sum = 0; f <= limit; t = f, f += pf, pf = t)
if (!(f & 1)) sum += f;
return sum;
}
console.log(evenFibSum(4000000));
在处理整个列表时递归生成每个值的工作太多了,而且迭代计算值非常简单。记住递归定义有帮助,但对我来说它似乎不必要的复杂。python可能很慢,因为python一般都很慢。@RobertHarvey python没有那么慢。我100%使用它进行大量的数字处理,而且它的功能非常强大。问题在于实现,而不是语言。顺便说一句:您的Haskell实现是完全错误的。它应该是fibn=fibn-1+fibn-2,而不是fibn-1+fibn-2。事实上,您对fib的定义是不同的,即它永远循环,或者直到堆栈溢出。python解决方案在我的机器上运行不到10秒。修正后的Haskell解决方案运行约1秒。这里有些不对劲。@ZaidAjaj您应该确保您已经使用ghc-O2编译了它,以便在我们进行优化时使用它
陆上通信线。使用runhaskell或GHCi运行它将导致执行速度大大降低。另外,这个问题的更快实现是sum$filter,甚至$take,而他在问题中提供的Python解决方案并没有那么慢,除非他有一台非常旧的计算机。它在我的机器上运行3秒钟。这在python上运行得很好…我将尝试为我的机器上的其他实现找到类似的东西,在引入memonization之后,F解决方案需要0.005秒。只有当大部分执行在C实现中进行,而没有在Python和C之间进行大量切换时,Python才会快速运行。@mydogisbox您的评论完全证实了我所说的:如果引入memonization不会改变运行时,则意味着编译器已经引入了它。k*33调用所用的时间不可能与k'*2^32调用naive算法执行的调用次数相同,而不需要记忆。您可以看到python实现中的差异:从10秒到10微秒。另外,这两个实现都是纯python,没有对内置函数的调用,因此您关于切换到C的观点在这种情况下是完全无效的。我的观点是.net没有为您添加momoization,尽管这会很酷。出于某种原因,我没有看到您改进的python实现。当您迭代这些值时,您可以维护前一个值和当前值变量,并完全避免递归。