문제

문제 해결에서 Google 코드 잼(2009.1A.:"멀티 기본 행복")나가 어려운(코드의 현명한)솔루션에 관심이 있어요 그것은 어떻게 향상 시킬 수 있습니다.

문제를 설명하시는 것은:최소보다 더 큰 1 는 반복적으로 합산하는 사각형의 자리에 도달 1,에 대한 기준에서 주어진 목록입니다.

또 설명에서 의사켈(코드는 그것을 해결하는 경우 elem 수상 작업에 대한 무한한 목록):

solution =
  head . (`filter` [2..]) .
  all ((1 `elem`) . (`iterate` i) . sumSquareOfDigitsInBase)

내 어색한 솔루션:

  • 여 어색한 평가가 이런 종류의 코드: happy <- lift . lift . lift $ isHappy Set.empty base cur
  • 나는,그러나 다른 클라이언트의 결과를 isHappy 기능입니다.를 사용하여 주일에 대한 memoized 결과 지도입니다.
  • 을 찾으려고 첫 번째 솔루션을 사용 하지 않았 headfilter (다음과 같은 의사일 위에는 않습니다)때문에,계산지 않은 순수(상태가 변경).그래서 내가 반복 사용하여 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

다른 참가자를 사용하여 메가 더 좋은 해결책, 하지만 문제를 해결했다.나의 질문에 대한 반복적인 개선을 나의 코드입니다.

도움이 되었습니까?

해결책

귀하의 솔루션은 Monads의 사용 (및 남용)이 확실히 어색합니다.

  • 여러 변압기를 쌓아서 모나드를 구축하는 것이 일반적입니다.
  • 덜 평소이지만 때로는 여전히 발생합니다.
  • 여러 변압기를 쌓는 것은 매우 드문 일입니다
  • Maybet을 사용하여 루프를 방해하는 것이 훨씬 더 드문 일입니다.

코드가 너무 무의미합니다.

(`when` mzero) . isJust =<<
   runMaybeT (mapM_ f bases)

읽기 쉬운 대신

let isHappy = isJust $ runMaybeT (mapM_ f bases)
when isHappy mzero

이제 기능 solve1에 초점을 맞추려면 단순화하겠습니다. 그렇게 쉬운 방법은 내부 Maybet Monad를 제거하는 것입니다. 행복한 숫자가 발견 될 때 깨지는 영원한 루프 대신, 다른 길을 가고 숫자가 행복하지 않은 경우에만 되 살릴 수 있습니다.

또한, 당신은 실제로 주 모나드가 필요하지 않습니까? 하나는 항상 상태를 명백한 주장으로 대체 할 수 있습니다.

이러한 아이디어를 적용하는 것은 이제 훨씬 좋아 보인다 :

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

다른 팁

Monad* 클래스는 반복적 인 리프팅의 필요성을 제거하기 위해 존재합니다. 다음과 같은 서명을 변경하는 경우 :

type IsHappyMemo = Map.Map (Integer, Integer) Bool

isHappy :: MonadState IsHappyMemo m => Set.Set Integer -> Integer -> Integer -> m Bool

이렇게하면 대부분의 리프트를 제거 할 수 있습니다. 그러나 가장 긴 리프트 시퀀스는 스테이트 내부의 상태 모나드이기 때문에 제거 할 수 없으므로 Monadstate 유형 클래스를 사용하면 내부 상태에 도달 해야하는 외부 스타트를 제공합니다. 당신은 당신의 주 모나드를 새로운 유형으로 감싸고 기존 모나드 수업과 비슷한 모나 디 행복 수업을 만들 수 있습니다.

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 실행 monadic 조건자를 위한 모든 요소의 [2..], 는 무한 목록입니다.

로 monadic 목록 filterL cond (fromList [2..]) 목록을 나타내는 우리에 액세스할 수 있 한 번에 하나의 항목으로 monadic 작업,그래서 우리의 monadic 조건자 cond 지 않습니다 실제로 실행(과에 영향을 미치는 상태)지 않으면 우리가 소비하는 목록은 해당 항목입니다.

마찬가지로,구현 condandL 우리가하지 않을 계산 및 업데이트 상태는 경우에 우리는 이미 받았 False 결과 중 하나에서 isHappy Set.empty num 을 계산합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top