我正在做project euler question 136,并提出以下内容来测试给出的示例:
module Main where
import Data.List
unsum x y z n = (y > 0) && (z > 0) && (((x*x) - (y*y)- (z*z)) == n) && ((x - y) == (y - z))
answer = snub $ takeWhile (<100) [n|x<-[1..],d<-[1..x`div`2],n<-[x..100],y<-[x-d],z<-[y-d], unsum x y z n ]
where
snub [] = []
snub (x:xs) | elem x xs = snub (filter (/=x) xs)
| otherwise = x : snub xs
snub
将从列表中删除任何重复的数字。
该示例应该为n
提供25个解决方案x^2 - y^2 - z^2 == n
,所有数字都是正数(或者我从问题中收集),并且是x-y == y-z
的算术级数。但是当我使用代码时,会返回一个包含n
的11个解决方案的列表。
我在列表理解中做错了什么,是否有任何我错过的优化?
答案 0 :(得分:2)
我尝试了这个问题,发现这是我提出的n
s序列
[4,3,16,12,7,20,11,48,28,19,80,44,23,52,112,31,68,76,1156,43,176,559...
这可能意味着您的takeWhile (<100)
是错误的过滤功能,用于确定何时停止。在相关的说明中,我试着运行这个:
answer = snub $ filter (<=100) $ takeWhile (<200) [...listcomprehension...]
但我放弃了,因为它花了太长时间。这引导我指出第2点。
就优化而言,请查看列表理解在原始输出方面产生的结果。
Main> take 30 [(x,y,z,n) | x<-[1..], d<-[1..x`div`2], n<-[x..100], y<-[x-d], z<-[y-d]]
[(2,1,0,2),(2,1,0,3),(2,1,0,4),(2,1,0,5),(2,1,0,6),(2,1,0,7),(2,1,0,8),(2,1,0,9),
(2,1,0,10),(2,1,0,11),(2,1,0,12),(2,1,0,13),(2,1,0,14),(2,1,0,15),(2,1,0,16),(2,1,0,17),
(2,1,0,18),(2,1,0,19),(2,1,0,20),(2,1,0,21),(2,1,0,22),(2,1,0,23),(2,1,0,24),(2,1,0,25),
(2,1,0,26),(2,1,0,27),(2,1,0,28),(2,1,0,29),(2,1,0,30),(2,1,0,31)]
这意味着在x y z和n的每个组合上调用unsum,这有点多余,因为我们知道2^2 - 1^2 - 0^2 = 3
。
将n
的计算从列表推导(由于上面的速度慢)移动到函数并且仅列出包含有效的(x,y,z)
组合的列表也更简单且更少冗余。
ns = map nsum [(x, x-d, x-d-d) | x <- [1..], d <- [1..x`div`2]]
nsum (x,y,z) = x^2 - y^2 - z^2
然后可以从这个无限列表中计算出答案,但要注意使用takewhile。