Skip to content

Commit ddebfdb

Browse files
committed
feat(2024/Day11): part2 haskell + python
1 parent 3a041dc commit ddebfdb

File tree

3 files changed

+119
-15
lines changed

3 files changed

+119
-15
lines changed

2024/Day11/Day11.hs

+71-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module Main where
22

33
import Control.Parallel.Strategies
4+
import Data.Map (Map)
5+
import Data.Map qualified as M
46
import Debug.Trace
57
import System.Environment
68

@@ -11,37 +13,52 @@ type Output = Int
1113
parseInput :: String -> Input
1214
parseInput = map read . words . head . lines
1315

16+
-- Number of digits in a number : digitCount 128 = 3
1417
digitCount :: Int -> Int
1518
digitCount i
1619
| i < 10 = 1
1720
| otherwise = 1 + digitCount (div i 10)
1821

19-
splitNumber :: Int -> Int -> [Int]
20-
splitNumber n lengthn = [firstHalf, secondHalf]
22+
-- Splits a number containing an even number of digits in 2 : splitNumber 2048 = [20, 48]
23+
splitNumber :: Int -> [Int]
24+
splitNumber n = [firstHalf, secondHalf]
2125
where
26+
lengthn = {-# SCC stoneLength #-} digitCount n
2227
powered10 = {-# SCC powered10 #-} (10 ^ (div lengthn 2))
2328
firstHalf = {-# SCC firstHalf #-} div n powered10
2429
secondHalf = {-# SCC secondHalf #-} n - (firstHalf * powered10)
2530

26-
iterateStones :: [Int] -> [Int]
27-
iterateStones stones = concat $ map iterateStone stones
31+
-- Main Algorithm of the day
32+
iterateStone :: Int -> [Int]
33+
iterateStone 0 = [1]
34+
iterateStone stone
35+
| (== 0) . (`mod` 2) $ digitCount stone = splitNumber stone
36+
| otherwise = [stone * 2024]
37+
38+
solveFold :: Map (Int, Int) Int -> (Int, Int) -> Map (Int, Int) Int
39+
solveFold memRes (stone, n)
40+
| (stone, n) `M.member` memRes = memRes
41+
| n == 1 = M.insert (stone, n) (length newStones) memRes
42+
| otherwise = M.insert (stone, n) total memResFold
2843
where
29-
iterateStone 0 = [1]
30-
iterateStone stone
31-
| isSplitable = splitNumber stone stoneLength
32-
| otherwise = [stone * 2024]
33-
where
34-
isSplitable = {-# SCC isSplitable #-} (== 0) $ (`mod` 2) stoneLength
35-
stoneLength = {-# SCC stoneLength #-} digitCount stone
44+
newStones = iterateStone stone
45+
newQuerries = zip newStones $ repeat (n - 1)
46+
47+
memResFold = foldl solveFold memRes $ newQuerries
48+
49+
total = sum . map (memResFold M.!) $ newQuerries
3650

3751
solve :: Int -> Input -> Output
38-
solve n arr = length . fst . head . drop n $ iterate (\(l, i) -> trace (show (i, length l)) (iterateStones l, i + 1)) (arr, 0)
52+
solve n arr = sum $ map (solutions M.!) . zip arr $ repeat n
53+
where
54+
solutions :: Map (Int, Int) Int
55+
solutions = foldl solveFold M.empty . zip arr $ repeat n
3956

4057
part1 :: Input -> Output
4158
part1 = solve 25
4259

4360
part2 :: Input -> Output
44-
part2 = solve 40
61+
part2 = solve 75
4562

4663
main :: IO ()
4764
main = do
@@ -51,5 +68,44 @@ main = do
5168

5269
print input
5370

54-
print $ part1 input
55-
print $ (== 11965325) $ part2 input
71+
let res1 = part1 input
72+
-- print res1
73+
print $ any (== res1) [55312, 183484]
74+
let res2 = part2 input
75+
-- print res2
76+
print $ any (== res2) [65601038650482, 218817038947400]
77+
78+
79+
80+
81+
-- Gets main algorithm using a memory map to avoid calculating the same result twice
82+
-- lookUpIterateStone :: Map Int [Int] -> Int -> (Map Int [Int], [Int])
83+
-- lookUpIterateStone mem n
84+
-- | n `M.member` mem = (mem, mem M.! n)
85+
-- | otherwise = (newMem, newArr)
86+
-- where
87+
-- newArr = iterateStone n
88+
-- newMem = M.insert n newArr mem
89+
90+
-- solveFold :: (Map (Int, Int) Int, Map Int [Int]) -> (Int, Int) -> (Map (Int, Int) Int, Map Int [Int])
91+
-- solveFold (memRes, mem1) (stone, n)
92+
-- | (stone, n) `M.member` memRes = (memRes, mem1)
93+
-- | n == 1 = (M.insert (stone, n) (length resLookUp) memRes, mem1LookUp)
94+
-- | otherwise = (M.insert (stone, n) total memResFold, mem1Fold)
95+
-- where
96+
-- (mem1LookUp, resLookUp) = lookUpIterateStone mem1 stone
97+
-- (memResSub1, mem1Sub1) = solveFold (memRes, mem1) (stone, n - 1)
98+
99+
-- newStones :: [Int]
100+
-- newStones = mem1Sub1 M.! stone
101+
102+
-- newQuerries :: [(Int, Int)]
103+
-- newQuerries = zip newStones $ repeat (n - 1)
104+
-- (memResFold, mem1Fold) = foldl solveFold (memResSub1, mem1Sub1) $ newQuerries
105+
-- total = sum . map (memResFold M.!) $ newQuerries
106+
107+
-- solve :: Int -> Input -> Output
108+
-- solve n arr = sum $ map (solutions M.!) . zip arr $ repeat n
109+
-- where
110+
-- solutions :: Map (Int, Int) Int
111+
-- solutions = fst . foldl solveFold (M.empty, M.empty) . zip arr $ repeat n

2024/Day11/Day11.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import sys
2+
3+
def algo(stone) :
4+
if stone == 0 :
5+
return [1]
6+
elif len(str(stone)) % 2 == 0 :
7+
half_len = len(str(stone)) // 2
8+
return [int(str(stone)[:half_len]), int(str(stone)[half_len:])]
9+
else :
10+
return [2024*stone]
11+
12+
solvei_cache = {}
13+
def solvei(stone, n) :
14+
if (stone, n) in solvei_cache :
15+
return solvei_cache[(stone, n)]
16+
res1 = algo(stone)
17+
if n == 1 :
18+
solvei_cache[(stone, n)] = len(res1)
19+
return len(res1)
20+
21+
solvei_cache[(stone, n)] = sum(map (lambda x : solvei(x, n-1), res1))
22+
return solvei_cache[(stone, n)]
23+
24+
def solve(arr, n) :
25+
return sum(map(lambda x : solvei(x, n), arr))
26+
27+
def part1(arr) :
28+
return solve(arr, 25)
29+
30+
def part2(arr) :
31+
return solve(arr, 75)
32+
33+
if __name__ == "__main__" :
34+
file = sys.argv[1]
35+
with open(file) as f :
36+
arr = list(map(int, f.read().split(' ')))
37+
print(part1(arr))
38+
print(part2(arr))

2024/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,13 @@ Tried to multithread it (in the `iterateStones` change `map` to `parMap rseq`) b
147147
- rpar : 12.7
148148

149149
Might try looking for loops and similar things
150+
151+
I finally got it, the solution I found was to use memoization
152+
153+
At first I had 2 maps :
154+
- First map for the result of the algorithm given an element (not much gain)
155+
- Second one for the number of stones after n blinks with the stone
156+
157+
Since the first map didn't help, I tried to clean up and realized it was really useless so I removed it completely
158+
159+
Had fun doing it in python as well, strangely python is faster than haskell

0 commit comments

Comments
 (0)