Initial commit

This commit is contained in:
Geoffrey Frogeye 2024-12-25 12:58:02 +01:00
commit 97a4330bc0
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8
110 changed files with 7006 additions and 0 deletions

20
2024/22/README.md Normal file
View 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
View 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
View 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
View 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
View 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)