Project Euler Question 14 (Collatz Problem)

后端 未结 8 1861
清酒与你
清酒与你 2020-11-27 07:58

The following iterative sequence is defined for the set of positive integers:

n ->n/2 (n is even) n ->3n + 1 (n is odd)

Using the rule above and starting wit

相关标签:
8条回答
  • 2020-11-27 08:25

    Haskell solution, 2 second run time.

    thomashartman@yucca:~/collatz>ghc -O3 -fforce-recomp --make collatz.hs
    [1 of 1] Compiling Main             ( collatz.hs, collatz.o )
    Linking collatz ...
    thomashartman@yucca:~/collatz>time ./collatz
    SPOILER REDACTED
    real    0m2.881s
    

    -- Maybe I could have gotten it a bit faster using a hash instead of a map.

    import qualified Data.Map as M
    import Control.Monad.State.Strict
    import Data.List (maximumBy)
    import Data.Function (on)
    
    nextCollatz :: Integer -> Integer
    nextCollatz n | even n = n `div` 2
                   | otherwise = 3 * n + 1
    
    newtype CollatzLength = CollatzLength Integer
      deriving (Read,Show,Eq,Ord)
    
    main = print longestCollatzSequenceUnderAMill 
    longestCollatzSequenceUnderAMill = longestCollatzLength [1..1000000]
    -- sanity checks
    tCollatzLengthNaive = CollatzLength 10 == collatzLengthNaive 13 
    tCollatzLengthMemoized = (CollatzLength 10) == evalState (collatzLengthMemoized 13) M.empty
    
    -- theoretically could be nonterminating. Since we're not in Agda, we'll not worry about it.
    collatzLengthNaive :: Integer -> CollatzLength
    collatzLengthNaive 1 = CollatzLength 1
    collatzLengthNaive n = let CollatzLength nextLength = collatzLengthNaive (nextCollatz n)
                           in  CollatzLength $ 1 + nextLength
    
    -- maybe it would be better to use hash here?
    type CollatzLengthDb = M.Map Integer CollatzLength
    type CollatzLengthState = State CollatzLengthDb 
    
    -- handy for testing
    cLM :: Integer -> CollatzLength
    cLM n = flip evalState M.empty $ (collatzLengthMemoized n) 
    
    collatzLengthMemoized :: Integer -> CollatzLengthState CollatzLength
    collatzLengthMemoized 1 = return $ CollatzLength 1
    collatzLengthMemoized n = do
      lengthsdb <- get
      case M.lookup n lengthsdb of 
        Nothing -> do let n' = nextCollatz n 
                      CollatzLength lengthN' <- collatzLengthMemoized n'
                      put $ M.insert n' (CollatzLength lengthN') lengthsdb
                      return $ CollatzLength $ lengthN' + 1
        Just lengthN -> return lengthN
    
    longestCollatzLength :: [Integer] -> (Integer,CollatzLength)
    longestCollatzLength xs = flip evalState M.empty $ do 
      foldM f (1,CollatzLength 1) xs
      where f maxSoFar@(maxN,lengthMaxN) nextN = do
              lengthNextN <- collatzLengthMemoized nextN
              let newMaxCandidate = (nextN,lengthNextN)
              return $ maximumBy (compare `on` snd) [maxSoFar, newMaxCandidate]
    

    ================================================================================

    And here is another haskell solution, using monad-memo package. Unfortunately, this one has a stack space error that does not affect the rolled-my-own memoizer above.

    ./collatzMemo +RTS -K83886080 -RTS # this produces the answer, but it would be bettter to eliminate the space leak

    {-# Language GADTs, TypeOperators #-} 
    
    import Control.Monad.Memo
    import Data.List (maximumBy)
    import Data.Function (on)
    
    nextCollatz :: Integer -> Integer
    nextCollatz n | even n = n `div` 2
                   | otherwise = 3 * n + 1
    
    newtype CollatzLength = CollatzLength Integer
      deriving (Read,Show,Eq,Ord)
    
    main = print longestCollatzSequenceUnderAMill 
    longestCollatzSequenceUnderAMill = longestCollatzLength [1..1000000]
    
    collatzLengthMemoized :: Integer -> Memo Integer CollatzLength CollatzLength
    collatzLengthMemoized 1 = return $ CollatzLength 1
    collatzLengthMemoized n = do
      CollatzLength nextLength <- memo collatzLengthMemoized (nextCollatz n)
      return $ CollatzLength $ 1 + nextLength 
    {- Stack space error
    ./collatzMemo
    Stack space overflow: current size 8388608 bytes.
    Use `+RTS -Ksize -RTS' to increase it.
    
    Stack error does not effect rolled-my-own memoizer at
    http://stackoverflow.com/questions/2643260/project-euler-question-14-collatz-problem
    -}
    longestCollatzLength :: [Integer] -> (Integer,CollatzLength)
    longestCollatzLength xs = startEvalMemo $ do
      foldM f (1,CollatzLength 1) xs
      where f maxSoFar nextN = do
              lengthNextN <- collatzLengthMemoized nextN
              let newMaxCandidate = (nextN,lengthNextN)
              return $ maximumBy (compare `on` snd) [maxSoFar, newMaxCandidate]
    
    {-
    -- sanity checks
    tCollatzLengthNaive = CollatzLength 10 == collatzLengthNaive 13 
    tCollatzLengthMemoized = (CollatzLength 10) ==startEvalMemo (collatzLengthMemoized 13) 
    
    -- theoretically could be nonterminating. Since we're not in Agda, we'll not worry about it.
    collatzLengthNaive :: Integer -> CollatzLength
    collatzLengthNaive 1 = CollatzLength 1
    collatzLengthNaive n = let CollatzLength nextLength = collatzLengthNaive (nextCollatz n)
                           in  CollatzLength $ 1 + nextLength
    -}
    

    ==================================================

    another one, factored more nicely. doesn't run as fast but still well under a minute

    import qualified Data.Map as M
    import Control.Monad.State
    import Data.List (maximumBy, nubBy)
    import Data.Function (on)
    
    nextCollatz :: Integer -> Integer
    nextCollatz n | even n = n `div` 2
                   | otherwise = 3 * n + 1
    
    newtype CollatzLength = CollatzLength Integer
      deriving (Read,Show,Eq,Ord)
    
    main = print longestCollatzSequenceUnderAMillStreamy -- AllAtOnce                                                                                                                                                                                                         
    
    collatzes = evalState collatzesM M.empty
    longestCollatzSequenceUnderAMillAllAtOnce = winners . takeWhile ((<=1000000) .fst) $ collatzes
    longestCollatzSequenceUnderAMillStreamy = takeWhile ((<=1000000) .fst) . winners  $ collatzes
    
    
    -- sanity checks                                                                                                                                                                                                                                                          
    tCollatzLengthNaive = CollatzLength 10 == collatzLengthNaive 13
    tCollatzLengthMemoized = (CollatzLength 10) == evalState (collatzLengthMemoized 13) M.empty
    
    -- maybe it would be better to use hash here?                                                                                                                                                                                                                             
    type CollatzLengthDb = M.Map Integer CollatzLength
    type CollatzLengthState = State CollatzLengthDb
    
    collatzLengthMemoized :: Integer -> CollatzLengthState CollatzLength
    collatzLengthMemoized 1 = return $ CollatzLength 1
    collatzLengthMemoized n = do
      lengthsdb <- get
      case M.lookup n lengthsdb of
        Nothing -> do let n' = nextCollatz n
                      CollatzLength lengthN' <- collatzLengthMemoized n'
                      put $ M.insert n' (CollatzLength lengthN') lengthsdb
                      return $ CollatzLength $ lengthN' + 1
        Just lengthN -> return lengthN
    
    collatzesM :: CollatzLengthState [(Integer,CollatzLength)]
    collatzesM = mapM (\x -> do (CollatzLength l) <- collatzLengthMemoized x
                                return (x,(CollatzLength l)) ) [1..]
    
    winners :: Ord b => [(a, b)] -> [(a, b)]
    winners xs = (nubBy ( (==) `on` snd )) $ scanl1 (maxBy snd) xs
    
    maxBy :: Ord b => (a -> b) -> a -> a -> a
    maxBy f x y = if f x > f y then x else y
    
    0 讨论(0)
  • 2020-11-27 08:26

    Fixing the unsigned int issue in the original question.

    Added array for storing pre-computed values.

    include <stdio.h>                                                                                                                                     
    #define LIMIT 1000000
    
    unsigned int dp_array[LIMIT+1];
    
    unsigned int iteration(unsigned int value)
    {
     if(value%2==0)
      return (value/2);
     else
      return (3*value+1);
    }
    
    unsigned int count_iterations(unsigned int value)
    {
     int count=1;
     while(value!=1)
     {
     if ((value<=LIMIT) && (dp_array[value]!=0)){
       count+= (dp_array[value] -1);
       break;
      } else {
       value=iteration(value);
       count++;
      }
     }
     return count;
    }
    
    int main()
    {
     int iteration_count=0, max=0;
     int i,count;
     for(i=0;i<=LIMIT;i++){
      dp_array[i]=0;
     }
    
     for (i=1; i<LIMIT; i++)
     {
    //  printf("Current iteration : %d \t", i);
      iteration_count=count_iterations(i);
      dp_array[i]=iteration_count;
    //  printf(" %d \t", iteration_count);
      if (iteration_count>max)
       {
       max=iteration_count;
       count=i;
       }
    //  printf(" %d \n", max);
    
     }
    
     printf("Count = %d\ni = %d\n",max,count);
    
    }
    

    o/p: Count = 525 i = 837799

    0 讨论(0)
提交回复
热议问题