什么是影响列表理解中的因素的表现(处理时间)?

时间:2012-06-28 01:02:39

标签: performance haskell functional-programming

从Learn-you-a-haskell学习一个例子“哪个右三角形有 所有边的整数以及等于或小于10的所有边的周长都是24?

  

rightTrianglesOriginal = [(a,b,c)| c < - [1..10],b < - [1..10],a&lt; - [1..10],a ^ 2 + b ^ 2 == c ^ 2,a + b + c == 24]

我更改了原始示例的各个部分,并希望了解下面的过程(在极端条件下)。

  • 谓词的顺序会影响性能吗?

  • 添加谓词(其他谓词暗示)是否会影响性能? (例如a> b,c&gt; a,c&gt; b?)?

  • 根据谓词(1)a&gt;制作元组列表。 b和(2)c> a然后进一步应用^ 2 + b ^ 2 = c ^ 2会不会提升整体性能?

  • 如果我们更改参数位置,是否会对性能产生影响,例如(a,b,c)或(c,b,a)?

  • 如果需要如此重要的排列和组合,现实生活中应用的策略是什么?我们是否应该为下次使用存储预先计算的答案(尽可能)以提高性能或其他任何?< / p>

  

rightTriangles = [(a,b,c)| c&lt; - [1..10],b&lt; - [1..10],a&lt; - [1..10],a ^ 2 + b ^ 2 == c ^ 2]

几乎在很短的时间内给出结果。

  

rightTriangles10 = [(a,b,c)| c < - [1..10],b < - [1..10],a&lt; - [1..10],a ^ 2 + b ^ 2 == c ^ 2,a&gt; b,c> A]

几乎在很短的时间内给出结果。

  

rightTriangles100 = [(a,b,c)| c&lt; - [1..100],b&lt; - [1..100],a&lt; - [1..100],a ^ 2 + b ^ 2 == c ^ 2,a&gt; b,c> A]

在几秒钟内给出结果。

  

rightTriangles1000 = [(a,b,c)| c < - [1..1000],b < - [1..1000],a&lt; - [1..1000],a ^ 2 + b ^ 2 == c ^ 2,a&gt; b,c> A]

我在30分钟后停止了进程。结果尚未完成。

请注意,作为初学者,我缺乏检查处理单个功能所需的确切时间的知识。

1 个答案:

答案 0 :(得分:6)

rightTrianglesOriginal = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10], a^2 + b^2 == c^2, a+b+c == 24]
  • 谓词的顺序会影响性能吗?

这取决于。在这种情况下,改变谓词的顺序并没有改变任何实质性的东西,所以差异 - 如果有的话 - 会非常小。由于a^2 + b^2 == c^2检查比a + b + c == 24要贵一些,并且两个测试都过滤掉了很多值,我希望通过交换两个测试来实现小幅加速

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

但整个计算量很小,以至于很难可靠地进行测量。一般来说,通过重新排序测试和生成器可以获得很大的差异,特别是交错测试和生成器到快捷方式的死角会产生巨大的影响。在这里,您可以在b < cb生成器之间添加a测试以快捷方式。当然,将生成器更改为b <- [1 .. c-1]仍然会更有效。

  • 添加谓词(其他谓词另有暗示)是否会影响性能? (例如a > b, c > a, c > b?)?

是的,但通常很少,除非谓词的评估异常昂贵。在上面,如果谓词成立,你将对隐含的第三谓词进行不必要的评估。这里的谓词对于标准数字类型来说计算起来很便宜,并且它不经常评估(大多数候选三元组早期失败),因此影响几乎不可测量。但还有另外的工作要做 - 编译器不够聪明,无法消除它 - 因此需要额外的时间。

  • 根据谓词(1)a > b和(2)c > a制作元组列表,然后进一步应用^ 2 + b ^ 2 = c ^ 2将增强整体性能或不?

这取决于。如果将谓词放在可以快捷的位置,则可以提高性能。使用这些谓词需要重新排序生成器(在a之前获取b,因此您可以快速查看c > a)。计算比a^2 + b^2 == c^2便宜一点,所以即使测试总数增加(后者条件比前者多三倍),它仍然可以提高性能以便先进行更便宜的测试(但是,首先进行最具鉴别力的测试也可能是更好的策略,即使它们更昂贵,也取决于成本和功率之间的关系。)

  • 如果我们更改参数位置,会对性能产生影响吗(a,b,c)(c, b, a)

基本上,这不会产生任何可衡量的影响。

  • 如果需要这么大的排列和组合,现实生活中应用的策略是什么?我们是否应该为下次使用存储预先计算的答案(尽可能)以提高性能或其他?

这取决于。如果计算复杂且结果很小,则最好存储结果以供重用。如果计算很便宜并且结果很大,那么最好重新计算。在这种情况下,毕达哥拉斯三元组的数量很少,计算也不是很便宜,因此存储以便重复使用可能是有益的。

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

几乎在很短的时间内给出结果。

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

在几分钟内给出结果。

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

嗯,要检查的三元组数量是限制的三角形,因此将限制增加10倍会使检查的三元组数量增加1000倍,运行时间因素大致相同,由于更大的内存要求,它可能会略大一些。因此,即使没有编译,更不用说优化代码了,

ghci> [ (a,b,c) | c <- [1..100], b <- [1..100], a <- [1..100], a^2 + b^2 == c^2, a > b , c > a]
[(4,3,5),(8,6,10),(12,5,13),(12,9,15),(15,8,17),(16,12,20),(24,7,25),(20,15,25),(24,10,26)
,(21,20,29),(24,18,30),(30,16,34),(28,21,35),(35,12,37),(36,15,39),(32,24,40),(40,9,41)
,(36,27,45),(48,14,50),(40,30,50),(45,24,51),(48,20,52),(45,28,53),(44,33,55),(42,40,58)
,(48,36,60),(60,11,61),(63,16,65),(60,25,65),(56,33,65),(52,39,65),(60,32,68),(56,42,70)
,(55,48,73),(70,24,74),(72,21,75),(60,45,75),(72,30,78),(64,48,80),(80,18,82),(84,13,85)
,(77,36,85),(75,40,85),(68,51,85),(63,60,87),(80,39,89),(72,54,90),(84,35,91),(76,57,95)
,(72,65,97),(96,28,100),(80,60,100)]
(2.64 secs, 2012018624 bytes)

限制1000的预期时间约为45分钟。使用一些数据约束,我们可以更快地完成:

ghci> length [(a,b,c) | c <- [2 .. 1000], b <- [1 .. c-1], a <- [c-b+1 .. b], a*a + b*b == c*c]
881
(87.28 secs, 26144152480 bytes)