python/cnf_or_tuples.py
DomNomNomVR 028fb0bc3a dump
2025-04-14 15:58:38 +12:00

115 lines
3.7 KiB
Python

################ solver
from itertools import chain, combinations
from functools import reduce
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
def check_solution(selection, operator):
return reduce(
lambda a, b: tuple(operator(x, y) for x, y in zip(a, b)), selection
)
def tuple_selector(tuples, operator):
solution_count = 0
for selection in powerset(tuples):
if not selection:
continue
if all(check_solution(selection, operator)):
print(f"found a solution! {selection}")
solution_count += 1
print(f"{solution_count=}")
################# problem statement
def operator_with_secret(a: int, b: int) -> int:
either = {a, b}
if 1 in either:
return a + b % 2
# if 0 in either:
# return 0
# final element that must be included
if 2 in either:
# The x's represent free choices.
# 0bxxxxxxx00
secret = 0b010110100
return 3 if secret in either else 0
# build your own number kit
return a | b
tuple_selector(
[
# make a choice to set a bit in the first number
(0b000000100, 0, 1, 0, 0, 0, 0, 0, 0),
(0b000001000, 0, 0, 1, 0, 0, 0, 0, 0),
(0b000010000, 0, 0, 0, 1, 0, 0, 0, 0),
(0b000100000, 0, 0, 0, 0, 1, 0, 0, 0),
(0b001000000, 0, 0, 0, 0, 0, 1, 0, 0),
(0b010000000, 0, 0, 0, 0, 0, 0, 1, 0),
(0b100000000, 0, 0, 0, 0, 0, 0, 0, 1),
# .. or not
(0b000000000, 0, 1, 0, 0, 0, 0, 0, 0),
(0b000000000, 0, 0, 1, 0, 0, 0, 0, 0),
(0b000000000, 0, 0, 0, 1, 0, 0, 0, 0),
(0b000000000, 0, 0, 0, 0, 1, 0, 0, 0),
(0b000000000, 0, 0, 0, 0, 0, 1, 0, 0),
(0b000000000, 0, 0, 0, 0, 0, 0, 1, 0),
(0b000000000, 0, 0, 0, 0, 0, 0, 0, 1),
# this final element that must be included
(0b000000010, 1, 0, 0, 0, 0, 0, 0, 0),
],
operator_with_secret,
)
# Commutative operator variant
def operator_with_secret(a: int, b: int) -> int:
# We're going to create a bitpacked representation of the state of the
# computation. The state is the number of records included, and the value
# is a bitwise OR of the records included. The state is stored in the
# first slot of each tuple, but this operator doesn't know which slot is
# first, so we just check to see if the value is greater than 1.
value_bits = 9
total_records_to_select = 8
def extract(bitpacked: int) -> Tuple[int, int]:
count = bitpacked >> value_bits
value = bitpacked & ((1 << value_bits) - 1)
return (count if count else 1), value
def pack(count: int, value: int) -> int:
return (count << value_bits) | value
either = {a, b}
# If we're building our state in the first slot, handle it
if any(x > 1 for x in either):
# Unpack the state from each input
# State is just how many records have been included so far, and the
# value which has been built
ac, av = extract(a)
bc, bv = extract(b)
count, value = ac + bc, av | bv
# If we haven't inluded eight records yet, just build up the number
if count < total_records_to_select:
return pack(count, value)
# If we've included eight records, we're done and need to check the
# value is what we wanted
# The x's represent free choices.
# 0bxxxxxxx00
secret = 0b010110100
return 3 if value == secret else 0
# Otherwise, we're just checking that we select at least one truthy value
return a | b