I'm currently working on a program which computes amicable pairs (Project Euler Problem 21). I've already found the solution, however I noticed that a flaw in my program was that it evaluates all of the numbers of the set [1..] whether or not we have already found the number to be a pair.
i.e. If currently evaluating 220 and 284 is found to be it's pair, however continuing on through when the map function gets to 284 it shouldn't evaluate it again.
import Data.List
properDivisors :: (Integral a) => a -> [a]
properDivisors n = [x | x <- [1..n `div` 2],
n `mod` x == 0 ]
amicablePairOf :: (Integral a) => a -> Maybe a
amicablePairOf a
| a == b = Nothing
| a == dOf b = Just b
| otherwise = Nothing
where dOf x = sum (properDivisors开发者_Go百科 x)
b = dOf a
getAmicablePair :: (Integral a) => a -> [a]
getAmicablePair a = case amicablePairOf a of
Just b -> [a,b]
Nothing -> []
amicables = foldr (++) [] ams
where ams = map getAmicablePair [1..]
As an example:
take 4 amicables
returns:
[220,284,284,220]
I'm fairly new to Haskell and functional programming so forgive me if it an obvious solution.
Your problem is, that you try to safe work by outputting both amicable numbers. But actually, you don't safe very much, because your function still calculates for both numbers, whether they are amicable. Why not do it like this:
import Data.List
divSum :: (Integral a) => a -> [a]
divSum n = sum (filter (\a -> a `mod` n == 0) [1..n `div` 2])
isAmicable :: (Integral a) => a -> Bool
isAmicable a = a /= b && a == c where
b = divSum a
c = divSum b
amicables = filter isAmicable [1..]
Perhaps a slight modification in getAmicablePair
helps?
getAmicablePair :: (Integral a) => a -> [a]
getAmicablePair a = case amicablePairOf a of
Just b -> if a < b then [a,b] else []
Nothing -> []
... so you just get pairs with a smaller first element
精彩评论