好的,继续解决Project Euler上的问题,我仍然开始学习Haskell和编程。
我需要找到可被数字整除的最小数字1:20
所以我开始:
divides :: Int -> Int -> Bool
divides d n = rem n d == 0
divise n a | divides n a == 0 = n : divise n (a+1)
| otherwise = n : divise (n+1) a
我想要发生的是让它继续向上移动n值,直到一个神奇地被[1..20]整除。 但这不起作用,现在我被困在这里从哪里开始。我假设我需要使用: [1..20] 对于a的值,但我不知道如何实现它。
答案 0 :(得分:1)
好吧,最近我自己解决了欧拉问题,我很想发布我的答案,但现在我会弃权。 :)
现在,你的程序流程有点混乱,听起来像风水人。基本上,你试图做一件事:增加n直到1..20除以n。但实际上,您应该将其视为两个步骤。
目前,您的代码说:“如果a不除n,则递增n。如果a除以n,则递增a”。但那并不是你想说的。
你想(我想)说“增加n,看看它是否分开[编辑:所有数字1..20]。如果没有,再次增加n,再次测试,等等。”那么你想要做的是进行一个子测试:一个接受一个数字,然后根据1..20进行测试,然后返回一个结果。
希望这有帮助!享受欧拉问题!
编辑:我真的,真的应该记住所有的话。
答案 1 :(得分:1)
嗯,作为一种算法,这种方式很糟糕。
对不起。
但你被列表误导了。我认为你要做的是迭代所有可用的数字,直到找到一个[1..20]
中的所有数字。在上面的实现中,如果a不除n,你永远不会回去检查b< a为n + 1。
您的算法的任何简单实现都将是:
lcmAll :: [Int] -> Maybe Int
lcmAll nums = find (\n -> all (divides n) nums) [1..]
(使用Data.List.find
和Data.List.all
)。
更好的算法是使用foldl:
成对找到lcmlcmAll :: [Int] -> Int
lcmAll = foldl lcmPair 1
lcmPair :: Int -> Int -> Int
lcmPair a b = lcmPair' a b
where lcmPair' a' b' | a' < b' = lcmPair' (a+a') b'
| a' > b' = lcmPair' a' (b + b')
| otherwise = a'
当然,您可以使用Prelude中的lcm
函数而不是lcmPair。
这是有效的,因为任何一组数字的最小公倍数与[这些数字中两个的最小公倍数]和[其余数字]的最小公倍数相同
答案 2 :(得分:1)
函数'divise'永不停止,它没有基本情况。两个分支都调用divise,因此它们都是递归的。您还使用函数divides
,就像它将返回一个int(如rem
那样),但它返回Bool
。
我发现你已经开始将问题分成几部分,这通常有助于理解并使其更容易阅读。
另一件可以帮助的事情是编写函数的类型。如果您的功能有效,但您不确定其类型,请尝试使用ghci中的:i myFunction
。在这里,我修复了divides
中的类型错误(尽管还有其他错误):
*Main> :i divise
divise :: Int -> Int -> [Int] -- Defined at divise.hs:4:0-5
您希望它返回列表吗?
让您解决问题,尝试进一步将问题分成几部分。这是一种天真的方式:
检查一个数字是否可被另一个数字整除的函数。这是您的divides
功能。
检查数字是否可以被所有数字[1..20]分割的功能。
尝试迭代所有数字并在#2中的函数上尝试它们的函数。
答案 3 :(得分:0)
这是我的快速,更多Haskell-y方法,使用您的算法:
Prelude> let divisibleByUpTo i n = all (\x -> (i `rem` x) == 0) [1..n]
Prelude> take 1 $ filter (\x -> snd x == True) $ map (\x -> (x, divisibleByUpTo x 4)) [1..]
[(12,True)]
divisibleByUpTo返回一个布尔值,如果数字i
可以被n
的每个整数整除,类似于divides
函数。
对于Haskell的新人来说,下一行可能看起来很难,所以我会逐一解释:
从右侧开始,我们map (\x -> (x, divisibleByUpTo x 4)) [1..]
对每个号码x
说明从1开始,执行divisibleByUpTo x 4
并将其返回到(x, divisibleByUpTo x 4)
的元组中。我正在使用一个元组,所以我们知道哪个数字完全分开。
除此之外,我们有filter (\x -> snd x == True)
;意味着仅返回元组的第二项为True
的元素。
在声明的最左边,我们take 1
因为否则我们会有无限的结果列表。
这需要相当长的时间才能达到20的值。就像其他人说的那样,你需要一个更好的算法 - 考虑如何得到4的值,即使我们的“输入”数字是1-2-3-4 ,最终答案只是3 * 4的产物。想想为什么1和2从等式中“掉线”。