Initial commit
This commit is contained in:
commit
97a4330bc0
110 changed files with 7006 additions and 0 deletions
20
2024/22/README.md
Normal file
20
2024/22/README.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
Struggled on the fact that "rounding *down* to the **nearest** integer" doesn't mean `round()` it means `floor()` for part1.
|
||||
Got the idea in the back of my mind this is the thing that could be heavily paralellized.
|
||||
|
||||
For part2 I found a solution that would take 12h in brute force.
|
||||
Removing a list creation I got it down to 3 hours (`two_bf.py`). I let this run in the background.
|
||||
Using numpy shenanigans I got it down to 20 minutes.
|
||||
It worked before I could do further optimisation.
|
||||
|
||||
I have no fucking idea what could be the optimisations here,
|
||||
and not super interested in figuring out. I'll spoil myself.
|
||||
|
||||
---
|
||||
|
||||
OH
|
||||
|
||||
🤚: buyer × buyer's sequences
|
||||
👉: all possible sequences × buyers
|
||||
|
||||
Yeah alright
|
||||
|
29
2024/22/one.py
Normal file
29
2024/22/one.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
input_file = sys.argv[1]
|
||||
|
||||
with open(input_file) as fd:
|
||||
lines = [line.rstrip() for line in fd.readlines()]
|
||||
|
||||
|
||||
def prng(secwet: int) -> int:
|
||||
secwet ^= secwet * 64
|
||||
secwet %= 16777216
|
||||
secwet ^= secwet // 32
|
||||
secwet %= 16777216
|
||||
secwet ^= secwet * 2048
|
||||
secwet %= 16777216
|
||||
return secwet
|
||||
|
||||
|
||||
tot = 0
|
||||
for line in lines:
|
||||
secwet = int(line)
|
||||
print(f"Init {secwet=}")
|
||||
for i in range(2000):
|
||||
secwet = prng(secwet)
|
||||
print(f" -> {secwet=}")
|
||||
tot += secwet
|
||||
print(tot)
|
121
2024/22/two.py
Normal file
121
2024/22/two.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import rich.progress
|
||||
|
||||
input_file = sys.argv[1]
|
||||
|
||||
with open(input_file) as fd:
|
||||
lines = [line.rstrip() for line in fd.readlines()]
|
||||
|
||||
buyers = len(lines)
|
||||
|
||||
|
||||
def prng(secwet: int) -> int:
|
||||
secwet ^= secwet * 64
|
||||
secwet %= 16777216
|
||||
secwet ^= secwet // 32
|
||||
secwet %= 16777216
|
||||
secwet ^= secwet * 2048
|
||||
secwet %= 16777216
|
||||
return secwet
|
||||
|
||||
|
||||
buyers_ban: list[list[int]] = []
|
||||
buyers_dif: list[list[int]] = []
|
||||
|
||||
for line in rich.progress.track(lines, description="Calculating future banana prices"):
|
||||
secwet = int(line)
|
||||
buyer_ban: list[int] = []
|
||||
buyer_dif: list[int] = []
|
||||
last_ban = secwet % 10
|
||||
for i in range(2000):
|
||||
secwet = prng(secwet)
|
||||
ban = secwet % 10
|
||||
buyer_ban.append(ban)
|
||||
dif = ban - last_ban
|
||||
buyer_dif.append(dif)
|
||||
last_ban = ban
|
||||
# print(f"{secwet=} {ban=} {dif=}")
|
||||
# print(f"{buyer_ban=}")
|
||||
# print(f"{buyer_dif=}")
|
||||
buyers_ban.append(buyer_ban)
|
||||
buyers_dif.append(buyer_dif)
|
||||
|
||||
buyers_dif_np = np.array(buyers_dif)
|
||||
buyers_ban_np = np.array(buyers_ban)
|
||||
|
||||
sequence = tuple[int, int, int, int]
|
||||
|
||||
|
||||
def totbans(seq: sequence) -> int:
|
||||
match = None
|
||||
for i, num in enumerate(seq):
|
||||
nmatch = buyers_dif_np == num
|
||||
if match is not None:
|
||||
# nmatch = sp.ndimage.shift(nmatch, (0, -i))
|
||||
nmatch = np.roll(nmatch, -i, axis=1)
|
||||
nmatch &= match
|
||||
match = nmatch
|
||||
|
||||
# bans = buyers_ban_np * match
|
||||
# found = match.max(axis=1)
|
||||
# indexes = np.argmax(match > 0, axis=1)
|
||||
|
||||
tot = 0
|
||||
assert match is not None
|
||||
for b, buyer_match in enumerate(match):
|
||||
if not buyer_match.max():
|
||||
continue
|
||||
arg: int = np.argmax(buyer_match > 0)
|
||||
try:
|
||||
ban = buyers_ban_np[b, arg+3]
|
||||
tot += ban
|
||||
except IndexError:
|
||||
pass # shrug
|
||||
return tot
|
||||
|
||||
for buyer in range(buyers):
|
||||
buyer_dif = buyers_dif[buyer]
|
||||
for i in range(2000 - 4):
|
||||
if (
|
||||
buyer_dif[i] == seq[0]
|
||||
and buyer_dif[i + 1] == seq[1]
|
||||
and buyer_dif[i + 2] == seq[2]
|
||||
and buyer_dif[i + 3] == seq[3]
|
||||
):
|
||||
# if tuple(buyer_dif[i : i + 4]) == seq:
|
||||
tot += buyers_ban[buyer][i + 3]
|
||||
break
|
||||
return tot
|
||||
|
||||
|
||||
# print(f"{totbans((6, -1, -1, 0))=}") # demo0
|
||||
# print(f"{totbans((-2, 1, -1, 3))=}") # demo2 aoc
|
||||
# print(f"{totbans((6, -4, 4, -9))=}") # demo2 first
|
||||
|
||||
all_seqs: set[sequence] = set()
|
||||
|
||||
for buyer in rich.progress.track(
|
||||
range(buyers), description="Generating possible sequences"
|
||||
):
|
||||
buyer_dif = buyers_dif[buyer]
|
||||
for i in range(2000 - 4):
|
||||
seq: sequence = tuple(buyer_dif[i : i + 4])
|
||||
all_seqs.add(seq)
|
||||
print(f"{len(all_seqs)=}")
|
||||
|
||||
maxi = 0
|
||||
max_seq = None
|
||||
for seq in rich.progress.track(all_seqs, description="Finding score for sequences"):
|
||||
tb = totbans(seq)
|
||||
if tb > maxi:
|
||||
maxi = tb
|
||||
max_seq = seq
|
||||
|
||||
|
||||
print(f"{max_seq=}")
|
||||
print(maxi)
|
||||
# 1909 too low
|
87
2024/22/two_bf.py
Normal file
87
2024/22/two_bf.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
import rich.progress
|
||||
|
||||
input_file = sys.argv[1]
|
||||
|
||||
with open(input_file) as fd:
|
||||
lines = [line.rstrip() for line in fd.readlines()]
|
||||
|
||||
buyers = len(lines)
|
||||
|
||||
|
||||
def prng(secwet: int) -> int:
|
||||
secwet ^= secwet * 64
|
||||
secwet %= 16777216
|
||||
secwet ^= secwet // 32
|
||||
secwet %= 16777216
|
||||
secwet ^= secwet * 2048
|
||||
secwet %= 16777216
|
||||
return secwet
|
||||
|
||||
|
||||
buyers_ban: list[list[int]] = []
|
||||
buyers_dif: list[list[int]] = []
|
||||
|
||||
for line in rich.progress.track(lines, description="Calculating future banana prices"):
|
||||
secwet = int(line)
|
||||
buyer_ban: list[int] = []
|
||||
buyer_dif: list[int] = []
|
||||
last_ban = secwet % 10
|
||||
for i in range(2000):
|
||||
secwet = prng(secwet)
|
||||
ban = secwet % 10
|
||||
buyer_ban.append(ban)
|
||||
dif = ban - last_ban
|
||||
buyer_dif.append(dif)
|
||||
last_ban = ban
|
||||
# print(f"{secwet=} {ban=} {dif=}")
|
||||
# print(f"{buyer_ban=}")
|
||||
# print(f"{buyer_dif=}")
|
||||
buyers_ban.append(buyer_ban)
|
||||
buyers_dif.append(buyer_dif)
|
||||
|
||||
sequence = tuple[int, int, int, int]
|
||||
|
||||
|
||||
def totbans(seq: sequence) -> int:
|
||||
tot = 0
|
||||
for buyer in range(buyers):
|
||||
buyer_dif = buyers_dif[buyer]
|
||||
for i in range(2000 - 4):
|
||||
if (
|
||||
buyer_dif[i] == seq[0]
|
||||
and buyer_dif[i + 1] == seq[1]
|
||||
and buyer_dif[i + 2] == seq[2]
|
||||
and buyer_dif[i + 3] == seq[3]
|
||||
):
|
||||
# if tuple(buyer_dif[i : i + 4]) == seq:
|
||||
tot += buyers_ban[buyer][i + 3]
|
||||
break
|
||||
return tot
|
||||
|
||||
|
||||
all_seqs: set[sequence] = set()
|
||||
|
||||
for buyer in rich.progress.track(
|
||||
range(buyers), description="Generating possible sequences"
|
||||
):
|
||||
buyer_dif = buyers_dif[buyer]
|
||||
for i in range(2000 - 4):
|
||||
seq: sequence = tuple(buyer_dif[i : i + 4])
|
||||
all_seqs.add(seq)
|
||||
print(f"{len(all_seqs)=}")
|
||||
|
||||
maxi = 0
|
||||
max_seq = None
|
||||
for seq in rich.progress.track(all_seqs, description="Finding score for sequences"):
|
||||
tb = totbans(seq)
|
||||
if tb > maxi:
|
||||
maxi = tb
|
||||
max_seq = seq
|
||||
|
||||
|
||||
print(f"{max_seq=}")
|
||||
print(maxi)
|
112
2024/22/two_fast.py
Normal file
112
2024/22/two_fast.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import collections
|
||||
import math
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import rich.progress
|
||||
|
||||
dtype = np.int32
|
||||
|
||||
input_file = sys.argv[1]
|
||||
|
||||
with open(input_file) as fd:
|
||||
buyers_init_secret = np.array([int(line) for line in fd.readlines()], dtype=dtype)
|
||||
|
||||
buyers = len(buyers_init_secret)
|
||||
|
||||
ITERATIONS = 2000
|
||||
SEQUENCE_LENGTH = 4
|
||||
|
||||
|
||||
buyers_prices = np.ndarray((buyers, ITERATIONS), dtype=dtype)
|
||||
buyers_diffs = np.ndarray((buyers, ITERATIONS), dtype=dtype)
|
||||
|
||||
buyers_secret = buyers_init_secret.copy()
|
||||
last_buyers_price = buyers_init_secret % 10
|
||||
for i in rich.progress.track(range(ITERATIONS), description="Simulating stock market"):
|
||||
buyers_secret ^= buyers_secret * 64
|
||||
buyers_secret %= 16777216
|
||||
buyers_secret ^= buyers_secret // 32
|
||||
buyers_secret %= 16777216
|
||||
buyers_secret ^= buyers_secret * 2048
|
||||
buyers_secret %= 16777216
|
||||
buyers_price = buyers_secret % 10
|
||||
buyers_diff = buyers_price - last_buyers_price
|
||||
buyers_prices[:, i] = buyers_price
|
||||
buyers_diffs[:, i] = buyers_diff
|
||||
last_buyers_price = buyers_price
|
||||
|
||||
# Compress sequence tuples into a single integer
|
||||
|
||||
# Move to positive numbers
|
||||
trans = -buyers_diffs.min()
|
||||
buyers_diffs_translated = buyers_diffs + trans
|
||||
# Decide on a value to shift
|
||||
shift = math.ceil(math.log2(buyers_diffs_translated.max()))
|
||||
|
||||
buyers_sequences = buyers_diffs_translated.copy()
|
||||
for i in range(1, SEQUENCE_LENGTH):
|
||||
buyers_sequences += np.roll(buyers_diffs_translated << (shift * i), i, axis=1)
|
||||
# Make first few sequences invalid
|
||||
buyers_sequences[:, :SEQUENCE_LENGTH] = -1
|
||||
|
||||
# NEW (knowing it's best to iterate per buyer than per sequence) ~ 5 seconds
|
||||
# Inspired by the following, which is even faster, probably because data locality
|
||||
# achieves more than SIMD-fying?
|
||||
# https://github.com/mkern75/AdventOfCodePython/blob/23b6becdc873c6b865e783122a7dbce0b5f40f60/year2024/Day22.py
|
||||
|
||||
max_sequence = 1 << shift * SEQUENCE_LENGTH
|
||||
sequences_total = np.zeros(max_sequence, dtype=dtype)
|
||||
seen = np.zeros((buyers, max_sequence), dtype=bool)
|
||||
|
||||
for b in rich.progress.track(range(buyers), description="Evaluating each buyer"):
|
||||
for i in range(SEQUENCE_LENGTH, ITERATIONS):
|
||||
seq = buyers_sequences[b, i]
|
||||
|
||||
if seen[b, seq]:
|
||||
continue
|
||||
seen[b, seq] = True
|
||||
|
||||
sequences_total[seq] += buyers_prices[b, i]
|
||||
|
||||
print(f"{sequences_total.argmax()=}")
|
||||
print(sequences_total.max())
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# OLD (knowing you can compresses sequences only) ~ 1.5 minute
|
||||
|
||||
|
||||
def totbans(seq: int) -> int:
|
||||
match = buyers_sequences == seq
|
||||
found = match.max(axis=1)
|
||||
indexes = np.argmax(match, axis=1)
|
||||
bans = buyers_prices[range(buyers), indexes]
|
||||
bans *= found
|
||||
return bans.sum()
|
||||
|
||||
|
||||
def seq_to_int(seq: tuple[int, ...]) -> int:
|
||||
tot = 0
|
||||
for s, num in enumerate(seq):
|
||||
tot += (num + trans) << (SEQUENCE_LENGTH - s - 1) * shift
|
||||
return tot
|
||||
|
||||
|
||||
print(f"{totbans(seq_to_int((-2, 1, -1, 3)))=}")
|
||||
|
||||
all_seqs: set[int] = set(buyers_sequences.flat) - {-1}
|
||||
|
||||
maxi = 0
|
||||
max_seq = None
|
||||
for seq in rich.progress.track(all_seqs, description="Finding score for sequences"):
|
||||
tb = totbans(seq)
|
||||
if tb > maxi:
|
||||
maxi = tb
|
||||
max_seq = seq
|
||||
|
||||
|
||||
print(f"{max_seq=}")
|
||||
print(maxi)
|
Loading…
Add table
Add a link
Reference in a new issue