繁体   English   中英

Haskell列表理解是否效率低下?

[英]Are Haskell List Comprehensions Inefficient?

我开始做项目欧拉并得到问题9 由于我使用Project Euler来学习Haskell,我决定使用列表推导(如Learn You A Haskell中所示 )。 我这样做,GHCI需要一段时间来弄清楚三元组,由于涉及的计算,我认为这是正常的。 现在,昨天上班(我不是专业的程序员),我正在和一位了解VBA的朋友交谈,他想尝试在VBA中找到答案。 我认为这也是一个有趣的挑战,我为ch循环和if语句制作了一些基本的东西,但是我得到的是它比Haskell快得多。

我的问题是:Haskell的列表理解是否非常低效? 起初我以为这只是因为我处于GHC的交互模式,但后来我意识到VBA也被解释了。

请注意,我没有发布我的代码,因为它是项目euler的答案。 如果它会回答我的问题(因为我做错了),那么我很乐意发布代码。

[编辑]这是我的Haskell列表理解:
[(a,b,c) | c <- [1..1000], b <- [1..c], a <- [1..b], a+b+c=1000, a^2+b^2=c^2]
我想我可以降低c的范围,但这是真的放慢了它的速度吗?

你可以用这个问题做两件事,这会让你的代码变慢。 一个是你如何尝试a,b和c的值。 如果你将a,b,c的所有可能值从1循环到1000,那么你将花费很长时间。 要给出提示,如果将其重新排列为c,则可以使用+ b + c = 1000。 另一个是如果你只使用列表推导,它将处理a,b和c的每个可能的值。 问题告诉您,只有一组唯一的数字可以解决问题,因此如果您更改答案:

[ a * b * c | .... ]

至:

head [ a * b * c | ... ]

然后Haskell的懒惰评估意味着它会在找到第一个答案后停止。 当你找到第一个答案时,这就是Haskell等同于打破你的VBA循环。 当我使用这两个技巧时,我得到的答案很快就完成了(在一秒钟内)ghci。

附录:我最初错过了条件a <b <c。 您也可以在列表推导中使用它; 说出以下内容是有效的:

[(a, b) | b <- [1..100], a <- [1..b-1]]

考虑一下列表理解的简化版本:

[(a,b,c) | a <- [1..1000], b <- [1..1000], c <- [1..1000]]

这将给出a,b和c的所有可能组合 这有点像说,“三千面骰子可以通过多少种方式登陆?” 答案是1000 * 1000 * 1000 = 1,000,000,000种不同的组合。 如果生成每个组合需要0.001秒,那么完成所有组合需要1,000,000秒(~11.5天)。 (好吧,对于一台电脑来说,0.001秒实际上很慢,但你明白了)

将谓词添加到列表推导中时,它仍然需要相同的时间来计算; 事实上,它需要更长的时间,因为它需要检查它计算的10亿个组合中的每个组合的谓词。

现在考虑一下你的理解力。 它看起来应该快得多,对吧?

[(a,b,c) | c <- [1..1000], b <- [1..c], a <- [1..b], a+b+c=1000, a^2+b^2=c^2]

c有1000种选择。 b和a有多少? 那么,c的平均选择是500.对于c的所有选择,那么b的平均有500个选择(因为b的范围从1到c)。 同样,对于c和b的所有选择,a平均有250种选择。 这是非常手工波浪,但我相信它是准确的。 因此,对于a * 10亿/ 8~ = 1亿,b * 1000/4选择的c * 1000/2选择有1000种选择。 它的速度提高了8倍,但是如果你注意到它,你会注意到它实际上上面的简化版本相同。 如果我们比较同一问题的“简化”和“改进”版本,但是从[1..100000]而不是[1..1000],“改进”仍然只比“简化”快8倍。

不要误会我的意思,8x是一个很好的恒定因素加速。 但除非你想等几个小时才能得到解决方案,否则你需要得到一个更好的大哦。

正如尼尔指出,该办法以减少这一问题的复杂性,对于一个给定的bc ,选择a满足a+b+c=1000 这样一来,你不是想一堆的a s表示将失败。 降低大的复杂性; 你只会考虑大约1000 * 500 * 1 = 500,000组合,而不是~100,000,000。

一旦获得问题的解决方案,您可以在Project Euler站点上查看其他人的Haskell解决方案版本,以了解其他人如何解决问题。 顺便提一下,这里是引用问题的链接: http//projecteuler.net/index.php?section = problem& id = 9

除了其他人所说的关于在生成器中生成更少元素的内容之外,您还可以切换到使用Int而不是Integer作为数字的类型。 默认值为Integer,但您的数字足够小以适合Int。

(另外,对于nitpick,Haskell列表推导没有速度.Haskell是一种语言定义,具有非常少的操作语义。但是,特定的Haskell实现可能具有缓慢的列表推导。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM