Haskell 欧拉计划:解决问题的(更好)方法#5?

Haskell 欧拉计划:解决问题的(更好)方法#5?,haskell,Haskell,你可能知道Euler项目的第5个问题:获取可被所有数字1到20整除的最小数字 我应用的逻辑是“从大于列表(20)中最大值的第一个数字开始,也可以被它整除(40)”,步长为20(最大值) 我是用列表理解来做这件事的,但它很蹩脚 pe5 = head [x|x<-[40,60..],x`mod`3==0,x`mod`4==0,x`mod`6==0,x`mod`7==0,x`mod`8==0,x`mod`9==0,x`mod`11==0,x`mod`12==0,x`mod`13==0,x`

你可能知道Euler项目的第5个问题:获取可被所有数字1到20整除的最小数字

我应用的逻辑是“从大于列表(20)中最大值的第一个数字开始,也可以被它整除(40)”,步长为20(最大值)

我是用列表理解来做这件事的,但它很蹩脚

pe5 = head    [x|x<-[40,60..],x`mod`3==0,x`mod`4==0,x`mod`6==0,x`mod`7==0,x`mod`8==0,x`mod`9==0,x`mod`11==0,x`mod`12==0,x`mod`13==0,x`mod`14==0,x`mod`15==0,x`mod`16==0,x`mod`17==0,x`mod`18==0,x`mod`19==0] 

由于这是学习Haskell的一个练习,我只想指出,有一种更有效的方法可以从数学上解决这个问题,但我会让你自己去解决。相反,让我们使用您的逻辑解决Haskell中的问题

我简化了一点:

head [x | x <- [20,40..], length( filter ( \y -> x `mod` y /= 0) [1..20]) == 0]
head[x | x`mod`y/=0[1..20])==0]

这将在除数列表上创建一个过滤器,当它的长度为0时,意味着所有除数都除以x,因此这必须是我们的数字。这减少了示例中的一些混乱。注意,这种方法非常慢;你能想出一个更好的数学方法来解决这个问题吗?开始研究素数分解…

是的,你可以做得更好。首先,重写为

head [x | x<-[40,60..], all (\y -> x`mod`y == 0) [2..20] ]
head[x | x`mod`y==0[2..20]]

但这里真正需要的不是更圆滑的Haskell,而是更智能的算法。提示:使用算术的基本定理。然后,您的Haskell解决方案将以Eratosthenes的标准筛为例开始。

在这种特殊情况下,您可以使用
foldl
lcm
免费获得:

euler = foldl lcm 2 [3..20]

这给了我232792560个瞬间。因为扰流板已经贴好了,我想我应该解释一下它是如何工作的

可被两个数整除的最小数也称为这些数的最小公倍数。前奏曲中有一个函数用于计算这一点

λ> lcm 10 12
60
现在,为了将其扩展到多个数字,我们利用以下属性

lcm(a1,…an)=lcm(lcm(a1,…an-1),an)

在Haskell中,f(f(…f(a1,a2),…),an)可以写成
foldl1f[a1,a2,…an]
,因此我们可以用这个简单的一行程序解决这个问题:

λ> foldl1 lcm [1..20]
232792560

这在几分之一秒内就找到了解决方案。

没有算法变化,只需要一个较短的版本
head[x | x
rem`y==0)[1..20]`如果你想要一个更好的算法,你可以用
lcm
@is7s来思考。我没有要求更好的算法,但谢谢你的提示,一切都和“as”handler一样?顺便说一句,这个问题的标题是非描述性的(“此”部分可以进一步澄清)@hvr thanx,我会从现在起更加精确,我知道这有点太容易了。。我知道foldl(列表左侧的连续函数合成)的功能,我认为我的listlcm更容易(对于新手)阅读我的编辑@Vasu:也许对于初学者来说,但通常认为编写自己的递归是一种反模式,因为其中一个标准的高阶函数可以完成这项工作。虽然在某些情况下,这会降低代码的可读性,但这里的情况并非如此。foldl’可能更好(至少对于大型输入:)甚至更短:
foldl1lcm[2..20]
在看到这一点之前,我已经编写了
all(=0)$zipWith mod(repeat x)[3..20]
,但我认为你的更简洁。另一个将运行时间减半的简单优化是将
[2..20]
更改为
[11..19]
λ> foldl1 lcm [1..20]
232792560