如何将此作为单一功能编写?

时间:2013-05-05 17:15:34

标签: haskell

我正在参与Euler Q3项目并需要获得一个数字的最大素数因子。到目前为止,我已经得到了一对函数来返回给定数字的所有因子的列表,但这似乎是一个非常糟糕的方法(部分因为我只需要最大的)。

get_factors :: (Integral a) => a -> [a] -> [a]
get_factors _ [] = []
get_factors t (x:xs)
    | t `mod` x == 0 = x:get_factors t xs
    | otherwise = get_factors t xs

factors :: (Integral a) => a -> [a]
factors x = get_factors x [x,x-1..1]

> factors 1000
> [1000,500,250,200,125,100,50,40,25,20,10,8,5,4,2,1]

对我来说,如果你要启动递归功能,我需要有一个“启动”功能似乎很奇怪(或者有一个函数,我必须将它传递两次相同的值,再次,对我来说似乎很愚蠢)。

你能指出我应该如何做到这一点的正确方向吗?

3 个答案:

答案 0 :(得分:8)

您应该尝试认识到您在此处所做的事情,即从列表中选择满足某些条件的元素,这是一种非常常见的模式。此模式由Prelude中的filter函数实现。

使用filter,您可以将您的函数编写为:

factors n = filter (\d -> n `mod` d == 0) [n, n-1 .. 1]

或者,等效地,您可以使用列表理解:

factors n = [d | d <- [n, n-1 .. 1], n `mod` d == 0]

答案 1 :(得分:4)

使用“启动”函数调用递归函数在Haskell中很常见,所以不要害怕。通常它被写成

f = g someArgument
  where
    g = ...

在你的情况下

factors :: (Integral a) => a -> [a]
factors x = get_factors [x,x-1..1]
  where
    get_factors [] = []
    get_factors (y:ys)
        | x `mod` y == 0   = y : get_factors ys
        | otherwise        = get_factors ys

这表示您的代码的读者get_factors仅在此使用,而在其他任何地方使用,并帮助您保持代码清洁。 get_factors也可以访问x,这样可以简化设计。


其他一些想法:

  • 尝试除以所有数字是低效的。在这样的问题中,使用列表预先计算素数和因子列表要好得多。有many methods如何计算这样的列表,但出于教育目的,我建议你自己编写(这对于其他Project Euler问题会派上用场)。然后你可以得到素数列表,取一个小于或等于x的素数的一部分并尝试除以它们。
  • 在搜索最大因子时,您必须搜索1和x之间的所有素数。但如果x是复合的,则其中一个因素必须是<= sqrt(n)。您可以使用它来构建一个明显更好的算法。

答案 2 :(得分:2)

我不认为通过像[n,n-1 ..]这样的每个数字是个好主意,因为问题是600851475143.

largest_factors :: Integer -> Integer
largest_factors n = helper n 2
    where
        helper m p  
            | m < p^2 = m
            | m == p = m
            | m `mod` p == 0 = helper (m `div` p) p
            | otherwise = helper m (p+1)

我所做的是,一旦发现某个数字(比如p)除以数字n,它就会将它除以。这个在我的电脑上正常工作。这给了我一秒钟内的解决方案。