题
解决问题,从谷歌码果酱(2009.1答:"多基幸福")我想出了一个尴尬的(代码-明智的)解决方案,并且我感兴趣的是它如何可以改进。
问题的说明中,不久:发现的最小数量大于1为其迭代计算的平方和个数字达到1,所有的基地从一个给定名单。
或说明伪Haskell(代码,就会解决如果 elem
总是可以作为无限的清单):
solution =
head . (`filter` [2..]) .
all ((1 `elem`) . (`iterate` i) . sumSquareOfDigitsInBase)
和我尴尬的解决方案:
- 通过尴尬我是说它具有这样的代码:
happy <- lift . lift . lift $ isHappy Set.empty base cur
- 我memoize结果isHappy功能。使用国家单的memoized结果的地图。
- 试图找到第一个解决方案,我没有用
head
和filter
(喜欢的伪haskell上述会),因为计算不是纯粹的(变化的状态)。所以我迭代使用StateT带一个计数器和一MaybeT终止计算的条件时举行。 - 已经在里面了
MaybeT (StateT a (State b))
, ,如果情况不保持一个基地,没有必要检查的其他人,所以我有另一个MaybeT
在堆。
代码:
import Control.Monad.Maybe
import Control.Monad.State
import Data.Maybe
import qualified Data.Map as Map
import qualified Data.Set as Set
type IsHappyMemo = State (Map.Map (Integer, Integer) Bool)
isHappy :: Set.Set Integer -> Integer -> Integer -> IsHappyMemo Bool
isHappy _ _ 1 = return True
isHappy path base num = do
memo <- get
case Map.lookup (base, num) memo of
Just r -> return r
Nothing -> do
r <- calc
when (num < 1000) . modify $ Map.insert (base, num) r
return r
where
calc
| num `Set.member` path = return False
| otherwise = isHappy (Set.insert num path) base nxt
nxt =
sum . map ((^ (2::Int)) . (`mod` base)) .
takeWhile (not . (== 0)) . iterate (`div` base) $ num
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases =
fmap snd .
(`runStateT` 2) .
runMaybeT .
forever $ do
(`when` mzero) . isJust =<<
runMaybeT (mapM_ f bases)
lift $ modify (+ 1)
where
f base = do
cur <- lift . lift $ get
happy <- lift . lift . lift $ isHappy Set.empty base cur
unless happy mzero
solve :: [String] -> String
solve =
concat .
(`evalState` Map.empty) .
mapM f .
zip [1 :: Integer ..]
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s ++ "\n"
main :: IO ()
main =
getContents >>=
putStr . solve . tail . lines
其他的参赛选手使用Haskell没有 好的解决方案, 但解决的问题不同。我的问题是关于小迭代的改善,我的代码。
解决方案
你的解决方案是肯定很尴尬在其使用(和性虐待)的元:
- 通常是要建立一元零敲碎打叠的几个变压器
- 这是不平常,但仍然发生时,堆叠的几个国家
- 这是非常不寻常叠的几个可能的变压器
- 它甚至更不寻常的使用MaybeT以中断一个循环
你的代码是有点太无意义:
(`when` mzero) . isJust =<<
runMaybeT (mapM_ f bases)
而不是更容易阅读
let isHappy = isJust $ runMaybeT (mapM_ f bases)
when isHappy mzero
现在正把重点放在功能solve1,让我们简化它。一个简单的方法来这样做,是消除内MaybeT单.而不是一个永远的环打破当一个快乐的数被发现,你可以去周围的其他方法和recurse只有如果 数量并不高兴。
而且,你并不真正需要的国家单,你呢?人们总是可以替换的国家有一个明确的参数。
运用这些想法solve1现在看起来要好得多:
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases = go 2 where
go i = do happyBases <- mapM (\b -> isHappy Set.empty b i) bases
if and happyBases
then return i
else go (i+1)
我会更汉满意这个代码。其余的解决方案是好的。一件事困扰着我是你扔掉的备忘录,缓对每个子问题.还有一个原因是什么?
solve :: [String] -> String
solve =
concat .
(`evalState` Map.empty) .
mapM f .
zip [1 :: Integer ..]
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s ++ "\n"
不你的解决方案会更有效,如果你再用它,而不是?
solve :: [String] -> String
solve cases = (`evalState` Map.empty) $ do
solutions <- mapM f (zip [1 :: Integer ..] cases)
return (unlines solutions)
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s
其他提示
该单*类存在删除需要反复提升。如果你改变你的签名这样的:
type IsHappyMemo = Map.Map (Integer, Integer) Bool
isHappy :: MonadState IsHappyMemo m => Set.Set Integer -> Integer -> Integer -> m Bool
这样,你可以除去大的提升。然而,最长的序列的电梯不能删除,因为它是一个国家单内StateT,因此使用MonadState型类会给你的外StateT,在你需要tot得到内心状态。你可以把你的国家单在新和做MonadHappy级,类似于现有的单课程。
ListT
(从 列表 包)没有一个更好的工作比 MaybeT
在停止计算,必要时。
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases = do
Cons result _ <- runList . filterL cond $ fromList [2..]
return result
where
cond num = andL . mapL (isHappy Set.empty num) $ fromList bases
一些阐述在这怎么工作:
我们使用一个常规列表代码会有这样的:
solve1 bases = do
result:_ <- filterM cond [2..]
return result
where
cond num = fmap and . mapM (isHappy Set.empty num) bases
这个计算是发生在一个 State
单,但如果我们想获得生成的状态,我们就会有一个问题,因为 filterM
运行一元谓它变得对每一个元素 [2..]
, ,一个无限的清单。
有的单子列表, filterL cond (fromList [2..])
代表一个名单,我们就可以访问的一个项目的时间作为一个单子的行动,使我们的元谓 cond
不是实际执行(并影响国家)除非我们消费的相应清单的项目。
类似地,实施 cond
使用 andL
让我们不计算和更新国家如果我们已经有了一个 False
结果从一个的 isHappy Set.empty num
计算。