python/1-2-3-digit-addend-662288000.py
DomNomNomVR 028fb0bc3a dump
2025-04-14 15:58:38 +12:00

153 lines
4.2 KiB
Python

from functools import cache
from collections import Counter, defaultdict
# pool = Counter(map(int, "000226688"))
# counted_digits = (0, 2, 6, 8)
# counts = (3, 2, 2, 2)
# 268
# 208
# 60
# 0
@cache
def choose_n_unique(counts, n):
if sum(counts) < n:
raise "you done fucked up"
if sum(counts) == n:
return set([counts])
out = set([])
for i, count in enumerate(counts):
if count <= 0:
continue
out.update(
choose_n_unique(counts[:i] + (count - 1,) + counts[i + 1 :], n)
)
return out
def sub(a, b): # a-b
return tuple(x - y for x, y in zip(a, b))
# print(choose_n_unique(counts, 3))
# print(find_sums(counts))
# print(possible_sums((3, 2, 2, 2)))
def has_len_digit(tup, length):
for t in tup:
if len(str(t)) == length:
return True
return False
# tup_0, tup_1 = (826,), (0, 0, 6, 820)
# print(has_len_digit(tup_0, 2))
# print(has_len_digit(tup_1, 2))
# exit()
def find_solutions(counted_digits, counts):
@cache
def possible_sums(counts): # sum -> set(tuple(addend))
if sum(counts) == 0:
return {0: set()}
if sum(counts) == 1:
for i, count in enumerate(counts):
if count == 0:
continue
digit = counted_digits[i]
out = {digit: set([(digit,)])}
return out
out = defaultdict(set)
for i, count in enumerate(counts):
if count <= 0:
continue
digit = counted_digits[i]
sub = possible_sums(counts[:i] + (count - 1,) + counts[i + 1 :])
for s, tups in sub.items():
for tup in tups:
out[s + digit].add(tuple(sorted((digit,) + tup)))
# try appending the digit to all sub subms
for tups in sub.values():
for tup in tups:
for j, val in enumerate(tup):
if j > 0 and val == tup[j - 1]:
continue
new_tup = tuple(
sorted((val * 10 + digit,) + tup[:j] + tup[j + 1 :])
)
out[sum(new_tup)].add(new_tup)
return out
out = []
for counts_0 in choose_n_unique(counts, 3):
counts_1 = sub(counts, counts_0)
# print(counts_1)
possible_0 = possible_sums(counts_0)
possible_1 = possible_sums(counts_1)
# print(possible_0.get(268, "nope"))
# print(possible_1.get(268, "nope"))
# print()
for total in possible_0.keys() & possible_1.keys():
tups_0 = possible_0[total]
tups_1 = possible_1[total]
for tup_0 in tups_0:
for tup_1 in tups_1:
if not (has_len_digit(tup_0, 1) or has_len_digit(tup_1, 1)):
continue
if not (has_len_digit(tup_0, 2) or has_len_digit(tup_1, 2)):
continue
if not (has_len_digit(tup_0, 3) or has_len_digit(tup_1, 3)):
continue
out.append((tup_0, tup_1))
return out
# print(find_solutions(counted_digits=(0, 2, 6, 8), counts=(3, 2, 2, 2)))
import random
rng = random.Random(4)
# best = 100
# for i in range(1000):
# # rng.randint()
# def r(radius):
# return rng.randint(-radius, radius)
# candidate = find_solutions(
# counted_digits=(1 + r(1), 2 + r(1), 6 + r(1), 8 + r(1)),
# counts=(3 + r(1), 2 + r(1), 2 + r(1), 2 + r(1)),
# )
# if not candidate:
# continue
# if len(candidate) <= best:
# best = len(candidate)
# print("yay")
# for tup in candidate:
# print(" ", tup)
def counted_digits_and_counts_from_solution(solution):
s = str(solution)
for to_remove in "(), ":
s = s.replace(to_remove, "")
print(s)
print("".join(sorted(s)))
c = Counter(s)
counted_digits = tuple(int(k) for k in sorted(c))
counts = tuple(int(c[k]) for k in sorted(c))
return counted_digits, counts
print(counted_digits_and_counts_from_solution("((261,), (7, 27, 227)))"))
print(find_solutions((1, 2, 6, 7), (1, 4, 1, 3)))